From fc65a8ea8f9f374343cd3eb8c59fc955b75b013a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Mon, 7 Apr 2025 19:23:11 -0700 Subject: [PATCH 001/477] paramsd: cache backwards compatibility (#34991) * LiveParametersV2 key * Param key * Fix tests * Remove old params if error * Update exception message * Update the test * Add test for corrupted byte format in v1 * Rename params_reader => params --- common/params_keys.h | 1 + selfdrive/locationd/paramsd.py | 32 +++++++++++++----------- selfdrive/locationd/test/test_paramsd.py | 13 +++++++++- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 2b540b744c..fd701cda18 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -72,6 +72,7 @@ inline static std::unordered_map keys = { {"LastUpdateException", CLEAR_ON_MANAGER_START}, {"LastUpdateTime", PERSISTENT}, {"LiveParameters", PERSISTENT}, + {"LiveParametersV2", PERSISTENT}, {"LiveTorqueParameters", PERSISTENT | DONT_LOG}, {"LocationFilterInitialState", PERSISTENT}, {"LongitudinalManeuverMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index 243abc5c08..a7712ba1d9 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -200,26 +200,28 @@ def check_valid_with_hysteresis(current_valid: bool, val: float, threshold: floa # TODO: Remove this function after few releases (added in 0.9.9) -def migrate_cached_vehicle_params_if_needed(params_reader: Params): - last_parameters_data = params_reader.get("LiveParameters") - if last_parameters_data is None: +def migrate_cached_vehicle_params_if_needed(params: Params): + last_parameters_data_old = params.get("LiveParameters") + last_parameters_data = params.get("LiveParametersV2") + if last_parameters_data_old is None or last_parameters_data is not None: return try: - last_parameters_dict = json.loads(last_parameters_data) + last_parameters_dict = json.loads(last_parameters_data_old) last_parameters_msg = messaging.new_message('liveParameters') last_parameters_msg.liveParameters.valid = True last_parameters_msg.liveParameters.steerRatio = last_parameters_dict['steerRatio'] last_parameters_msg.liveParameters.stiffnessFactor = last_parameters_dict['stiffnessFactor'] last_parameters_msg.liveParameters.angleOffsetAverageDeg = last_parameters_dict['angleOffsetAverageDeg'] - params_reader.put("LiveParameters", last_parameters_msg.to_bytes()) - except Exception: - pass + params.put("LiveParametersV2", last_parameters_msg.to_bytes()) + except Exception as e: + cloudlog.error(f"Failed to perform parameter migration: {e}") + params.remove("LiveParameters") -def retrieve_initial_vehicle_params(params_reader: Params, CP: car.CarParams, replay: bool, debug: bool): - last_parameters_data = params_reader.get("LiveParameters") - last_carparams_data = params_reader.get("CarParamsPrevRoute") +def retrieve_initial_vehicle_params(params: Params, CP: car.CarParams, replay: bool, debug: bool): + last_parameters_data = params.get("LiveParametersV2") + last_carparams_data = params.get("CarParamsPrevRoute") steer_ratio, stiffness_factor, angle_offset_deg, p_initial = CP.steerRatio, 1.0, 0.0, None @@ -267,12 +269,12 @@ def main(): pm = messaging.PubMaster(['liveParameters']) sm = messaging.SubMaster(['livePose', 'liveCalibration', 'carState'], poll='livePose') - params_reader = Params() - CP = messaging.log_from_bytes(params_reader.get("CarParams", block=True), car.CarParams) + params = Params() + CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) - migrate_cached_vehicle_params_if_needed(params_reader) + migrate_cached_vehicle_params_if_needed(params) - steer_ratio, stiffness_factor, angle_offset_deg, pInitial = retrieve_initial_vehicle_params(params_reader, CP, REPLAY, DEBUG) + steer_ratio, stiffness_factor, angle_offset_deg, pInitial = retrieve_initial_vehicle_params(params, CP, REPLAY, DEBUG) learner = VehicleParamsLearner(CP, steer_ratio, stiffness_factor, np.radians(angle_offset_deg), pInitial) while True: @@ -288,7 +290,7 @@ def main(): msg_dat = msg.to_bytes() if sm.frame % 1200 == 0: # once a minute - params_reader.put_nonblocking("LiveParameters", msg_dat) + params.put_nonblocking("LiveParametersV2", msg_dat) pm.send('liveParameters', msg_dat) diff --git a/selfdrive/locationd/test/test_paramsd.py b/selfdrive/locationd/test/test_paramsd.py index 3c2bf9249a..2129bf4386 100644 --- a/selfdrive/locationd/test/test_paramsd.py +++ b/selfdrive/locationd/test/test_paramsd.py @@ -28,7 +28,7 @@ class TestParamsd: CP = next(m for m in lr if m.which() == "carParams").carParams msg = get_random_live_parameters(CP) - params.put("LiveParameters", msg.to_bytes()) + params.put("LiveParametersV2", msg.to_bytes()) params.put("CarParamsPrevRoute", CP.as_builder().to_bytes()) migrate_cached_vehicle_params_if_needed(params) # this is not tested here but should not mess anything up or throw an error @@ -49,9 +49,20 @@ class TestParamsd: msg = get_random_live_parameters(CP) params.put("LiveParameters", json.dumps(msg.liveParameters.to_dict())) params.put("CarParamsPrevRoute", CP.as_builder().to_bytes()) + params.remove("LiveParametersV2") migrate_cached_vehicle_params_if_needed(params) sr, sf, offset, _ = retrieve_initial_vehicle_params(params, CP, replay=True, debug=True) np.testing.assert_allclose(sr, msg.liveParameters.steerRatio) np.testing.assert_allclose(sf, msg.liveParameters.stiffnessFactor) np.testing.assert_allclose(offset, msg.liveParameters.angleOffsetAverageDeg) + assert params.get("LiveParametersV2") is not None + + def test_read_saved_params_corrupted_old_format(self): + params = Params() + params.put("LiveParameters", b'\x00\x00\x02\x00\x01\x00:F\xde\xed\xae;') + params.remove("LiveParametersV2") + + migrate_cached_vehicle_params_if_needed(params) + assert params.get("LiveParameters") is None + assert params.get("LiveParametersV2") is None From e4aa346259d9be4713c0d6b7bcd49ac88c277c1f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 8 Apr 2025 10:57:29 -0700 Subject: [PATCH 002/477] hardwared: give stuck LTE connections a kick This reverts commit e3e694096a3a79e663712797e559e729e6818c7c. --- system/hardware/hardwared.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index b6de91818e..7429091336 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -99,6 +99,7 @@ def touch_thread(end_event): def hw_state_thread(end_event, hw_queue): """Handles non critical hardware state, and sends over queue""" count = 0 + registered_count = 0 prev_hw_state = None modem_version = None @@ -149,6 +150,16 @@ def hw_state_thread(end_event, hw_queue): except queue.Full: pass + if AGNOS and (hw_state.network_info is not None) and (hw_state.network_info.get('state', None) == "REGISTERED"): + registered_count += 1 + else: + registered_count = 0 + + if registered_count > 10: + cloudlog.warning(f"Modem stuck in registered state {hw_state.network_info}, bringing connection back up") + os.system("nmcli conn up esim") + registered_count = 0 + if not modem_configured and HARDWARE.get_modem_version() is not None: cloudlog.warning("configuring modem") HARDWARE.configure_modem() From 84b0baf68b1fc65924c705cd9c3fc301b3030629 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 8 Apr 2025 11:04:00 -0700 Subject: [PATCH 003/477] Revert "hardwared: give stuck LTE connections a kick" This reverts commit e4aa346259d9be4713c0d6b7bcd49ac88c277c1f. --- system/hardware/hardwared.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index 7429091336..b6de91818e 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -99,7 +99,6 @@ def touch_thread(end_event): def hw_state_thread(end_event, hw_queue): """Handles non critical hardware state, and sends over queue""" count = 0 - registered_count = 0 prev_hw_state = None modem_version = None @@ -150,16 +149,6 @@ def hw_state_thread(end_event, hw_queue): except queue.Full: pass - if AGNOS and (hw_state.network_info is not None) and (hw_state.network_info.get('state', None) == "REGISTERED"): - registered_count += 1 - else: - registered_count = 0 - - if registered_count > 10: - cloudlog.warning(f"Modem stuck in registered state {hw_state.network_info}, bringing connection back up") - os.system("nmcli conn up esim") - registered_count = 0 - if not modem_configured and HARDWARE.get_modem_version() is not None: cloudlog.warning("configuring modem") HARDWARE.configure_modem() From 0b72ae3bc2e11290d95e6f2d9e8fac683e9b0a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Tue, 8 Apr 2025 20:12:52 -0700 Subject: [PATCH 004/477] process_replay: fix GM and replace TOYOTA3 (#34996) * Add volt and bolt param migration * Remove mazda due to invalid sensor timings * Fix the import * Add mazda to excluded * Bring back mazda, but add todo * New segment for TOYOTA3 * Fix * Update ref commit --- selfdrive/test/process_replay/migration.py | 3 +++ selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/test/process_replay/test_processes.py | 7 ++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/selfdrive/test/process_replay/migration.py b/selfdrive/test/process_replay/migration.py index e1d0352262..39d5452b6b 100644 --- a/selfdrive/test/process_replay/migration.py +++ b/selfdrive/test/process_replay/migration.py @@ -9,6 +9,7 @@ from opendbc.car.fingerprints import MIGRATION from opendbc.car.toyota.values import EPS_SCALE, ToyotaSafetyFlags from opendbc.car.ford.values import CAR as FORD, FordFlags, FordSafetyFlags from opendbc.car.hyundai.values import HyundaiSafetyFlags +from opendbc.car.gm.values import GMSafetyFlags from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.fill_model_msg import fill_xyz_poly, fill_lane_line_meta from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_encode_index @@ -274,6 +275,8 @@ def migrate_pandaStates(msgs): "TOYOTA_PRIUS": EPS_SCALE["TOYOTA_PRIUS"] | ToyotaSafetyFlags.STOCK_LONGITUDINAL, "TOYOTA_RAV4": EPS_SCALE["TOYOTA_RAV4"] | ToyotaSafetyFlags.ALT_BRAKE, "KIA_EV6": HyundaiSafetyFlags.EV_GAS | HyundaiSafetyFlags.CANFD_LKA_STEERING, + "CHEVROLET_VOLT": GMSafetyFlags.EV, + "CHEVROLET_BOLT_EUV": GMSafetyFlags.EV | GMSafetyFlags.HW_CAM, } # TODO: get new Ford route safety_param_migration |= dict.fromkeys((set(FORD) - FORD.with_flags(FordFlags.CANFD)), FordSafetyFlags.LONG_CONTROL) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 58ff170e74..d4b02296d6 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -887623a18d82088dc5ed9ecdced55eb0d3f718b1 \ No newline at end of file +37fd7afb99bf188c1e5375d01f011ae35821f640 \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 927f54c0c3..dfa7eb65a8 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -22,7 +22,7 @@ source_segments = [ ("HYUNDAI2", "d545129f3ca90f28|2022-11-07--20-43-08--3"), # HYUNDAI.HYUNDAI_KIA_EV6 (+ QCOM GPS) ("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.TOYOTA_PRIUS ("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.TOYOTA_RAV4 - ("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.TOYOTA_COROLLA_TSS2 + ("TOYOTA3", "8011d605be1cbb77|000000cc--8e8d8ec716--6"), # TOYOTA.TOYOTA_COROLLA_TSS2 ("HONDA", "eb140f119469d9ab|2021-06-12--10-46-24--27"), # HONDA.HONDA_CIVIC (NIDEC) ("HONDA2", "7d2244f34d1bbcda|2021-06-25--12-25-37--26"), # HONDA.HONDA_ACCORD (BOSCH) ("CHRYSLER", "4deb27de11bee626|2021-02-20--11-28-55--8"), # CHRYSLER.CHRYSLER_PACIFICA_2018_HYBRID @@ -32,6 +32,7 @@ source_segments = [ ("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.CHEVROLET_BOLT_EUV ("NISSAN", "35336926920f3571|2021-02-12--18-38-48--46"), # NISSAN.NISSAN_XTRAIL ("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.VOLKSWAGEN_GOLF + # FIXME the sensor timings are bad in mazda segment, we're not fully testing it, but it should be replaced ("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.MAZDA_CX9_2021 ("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), # FORD.FORD_BRONCO_SPORT_MK1 ("RIVIAN", "bc095dc92e101734|000000db--ee9fe46e57--1"), # RIVIAN.RIVIAN_R1_GEN1 @@ -46,7 +47,7 @@ segments = [ ("HYUNDAI2", "regen306779F6870|2024-10-03--04-03-23--0"), ("TOYOTA", "regen4A5115B248D|2025-03-04--03-21-43--0"), ("TOYOTA2", "regen6E484EDAB96|2024-08-30--02-47-37--0"), - ("TOYOTA3", "regen4CE950B0267|2024-08-30--02-51-30--0"), + ("TOYOTA3", "8011d605be1cbb77|000000cc--8e8d8ec716--6"), ("HONDA", "regenB8CABEC09CC|2025-03-04--03-32-55--0"), ("HONDA2", "regen4B38A7428CD|2024-08-30--02-56-31--0"), ("CHRYSLER", "regenF3DBBA9E8DF|2024-08-30--02-59-03--0"), @@ -62,7 +63,7 @@ segments = [ ] # dashcamOnly makes don't need to be tested until a full port is done -excluded_interfaces = ["mock", "tesla", "rivian"] +excluded_interfaces = ["mock", "tesla"] BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit") From 4c7d29b907b215a7d50c2be209047726ea0ae4e9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 8 Apr 2025 21:08:04 -0700 Subject: [PATCH 005/477] bump opendbc --- docs/CARS.md | 242 ++++++++++++++++++++++++++------------------------- opendbc_repo | 2 +- 2 files changed, 123 insertions(+), 121 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 648c2cb9d8..1af8974f30 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -11,12 +11,12 @@ A supported vehicle is one that just works when you install a comma device. All |Acura|ILX 2016-19|AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -28,7 +28,7 @@ A supported vehicle is one that just works when you install a comma device. All |Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chrysler|Pacifica Hybrid 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| -|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -54,20 +54,20 @@ A supported vehicle is one that just works when you install a comma device. All |Genesis|G70 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Genesis|G90 2017-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV60 (Performance Trim) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 Electrified (Australia Only) 2022[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 Electrified (with HDA II) 2023-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV80 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV60 (Advanced Trim) 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV60 (Performance Trim) 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 Electrified (Australia Only) 2022[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 Electrified (with HDA II) 2023-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV80 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Civic Hatchback 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -96,10 +96,10 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (with HDA II) 2022-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (without HDA II) 2022-24[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 6 (with HDA II) 2023-24[5](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (with HDA II) 2022-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (without HDA II) 2022-24[6](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 6 (with HDA II) 2023-24[6](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq Electric 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -110,11 +110,11 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Kona 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai O connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai O connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona Electric (with HDA II, Korea only) 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Kona Electric (with HDA II, Korea only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai I connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Nexo 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Santa Cruz 2022-24[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Santa Cruz 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Santa Fe 2021-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -122,36 +122,36 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Sonata Hybrid 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Staria 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Staria 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson 2023-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson 2022[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson 2023-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson Hybrid 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson Plug-in Hybrid 2024[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson Hybrid 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson Plug-in Hybrid 2024[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Carnival 2022-24[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Carnival (China only) 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Carnival 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Carnival (China only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (Southeast Asia only) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (with HDA II) 2022-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (without HDA II) 2022-24[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (Southeast Asia only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (with HDA II) 2022-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (without HDA II) 2022-24[6](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|K8 Hybrid (with HDA II) 2023[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|K8 Hybrid (with HDA II) 2023[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro EV 2023-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro EV 2023-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Hybrid 2018|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -162,11 +162,11 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento 2021-23[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento Hybrid 2021-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sportage 2023-24[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento 2021-23[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento Hybrid 2021-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento Plug-in Hybrid 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sportage 2023-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sportage Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Stinger 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -193,42 +193,42 @@ A supported vehicle is one that just works when you install a comma device. All |Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan[7](#footnotes)|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan[7](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan[7](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan[7](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Subaru|Ascent 2019-21|All[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Forester 2019-21|All[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Impreza 2017-19|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Impreza 2020-22|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Legacy 2020-22|All[6](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Outback 2020-22|All[6](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|XV 2018-19|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|XV 2020-21|EyeSight Driver Assistance[6](#footnotes)|openpilot available[1,7](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Škoda|Fabia 2022-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| -|Škoda|Kamiq 2021-23[10,12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| -|Škoda|Karoq 2019-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Kodiaq 2017-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia 2015-19[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia RS 2016[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia Scout 2017-19[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Scala 2020-23[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| -|Škoda|Superb 2015-22[12](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model 3 (with HW3) 2019-23[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model 3 (with HW4) 2024-25[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model Y (with HW3) 2020-23[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model Y (with HW4) 2024[8](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Forester 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Impreza 2017-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Impreza 2020-22|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Legacy 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|Outback 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|XV 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Subaru|XV 2020-21|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| +|Škoda|Fabia 2022-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| +|Škoda|Kamiq 2021-23[12,14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| +|Škoda|Karoq 2019-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Kodiaq 2017-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia 2015-19[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia RS 2016[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia Scout 2017-19[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Scala 2020-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| +|Škoda|Superb 2015-22[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla|Model Y (with HW4) 2024[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -241,8 +241,8 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|C-HR 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|C-HR Hybrid 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2018-20|All|Stock|0 mph[9](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2021-24|All|openpilot|0 mph[9](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2018-20|All|Stock|0 mph[11](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2021-24|All|openpilot|0 mph[11](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -274,58 +274,60 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|RAV4 Hybrid 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat 2015-22[11](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| -|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[14](#footnotes)|| -|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,13](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat 2015-22[13](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| +|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| +|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| ### Footnotes 1openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`.
2By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
3Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia.
-42019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
-5Requires a CAN FD panda kit if not using comma 3X for this CAN FD car.
-6In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.
-7Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.
-8Some 2023 model years have HW4. To check which hardware type your vehicle has, look for Autopilot computer under Software -> Additional Vehicle Information on your vehicle's touchscreen. See this page for more information.
-9openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
-10Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-11Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
-12Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality.
-13Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
-14Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
+4See more setup details for GM.
+52019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph.
+6Requires a CAN FD panda kit if not using comma 3X for this CAN FD car.
+7See more setup details for Nissan.
+8In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.
+9Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.
+10Some 2023 model years have HW4. To check which hardware type your vehicle has, look for Autopilot computer under Software -> Additional Vehicle Information on your vehicle's touchscreen. See this page for more information.
+11openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
+12Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
+13Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
+14Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality.
+15Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
+16Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
## Community Maintained Cars Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/). diff --git a/opendbc_repo b/opendbc_repo index 4179672dae..81820502a0 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4179672dae72e29b8ff9072d9be154515312e350 +Subproject commit 81820502a038b64e97caae81377ac65a3b86703e From 2cd0079abef0f654541dfc894e97b02829163577 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 8 Apr 2025 21:11:24 -0700 Subject: [PATCH 006/477] test_models: update relay malfunction check (#34999) relay is checked in fwd hook --- opendbc_repo | 2 +- selfdrive/car/tests/test_models.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 81820502a0..5a24ffee45 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 81820502a038b64e97caae81377ac65a3b86703e +Subproject commit 5a24ffee4549b857837df03a7faaf5f31236f787 diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 906fa99320..fa266328f6 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -240,6 +240,7 @@ class TestCarModelBase(unittest.TestCase): continue to_send = libsafety_py.make_CANPacket(msg.address, msg.src % 4, msg.dat) + self.safety.safety_fwd_hook(msg.src, msg.address) if self.safety.safety_rx_hook(to_send) != 1: failed_addrs[hex(msg.address)] += 1 From dec42839e38db9f09372a3ee4999d791beded1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Tue, 8 Apr 2025 21:13:02 -0700 Subject: [PATCH 007/477] process_replay: remove GM (volt) (#35000) Remove volt segment --- selfdrive/test/process_replay/test_processes.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index dfa7eb65a8..bb30ab2acd 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -28,8 +28,7 @@ source_segments = [ ("CHRYSLER", "4deb27de11bee626|2021-02-20--11-28-55--8"), # CHRYSLER.CHRYSLER_PACIFICA_2018_HYBRID ("RAM", "17fc16d840fe9d21|2023-04-26--13-28-44--5"), # CHRYSLER.RAM_1500_5TH_GEN ("SUBARU", "341dccd5359e3c97|2022-09-12--10-35-33--3"), # SUBARU.SUBARU_OUTBACK - ("GM", "0c58b6a25109da2b|2021-02-23--16-35-50--11"), # GM.CHEVROLET_VOLT - ("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.CHEVROLET_BOLT_EUV + ("GM", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.CHEVROLET_BOLT_EUV ("NISSAN", "35336926920f3571|2021-02-12--18-38-48--46"), # NISSAN.NISSAN_XTRAIL ("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.VOLKSWAGEN_GOLF # FIXME the sensor timings are bad in mazda segment, we're not fully testing it, but it should be replaced @@ -53,8 +52,7 @@ segments = [ ("CHRYSLER", "regenF3DBBA9E8DF|2024-08-30--02-59-03--0"), ("RAM", "regenDB02684E00A|2024-08-30--03-02-54--0"), ("SUBARU", "regen5E3347D0A0F|2025-03-04--03-23-55--0"), - ("GM", "regen720F2BA4CF6|2024-08-30--03-09-15--0"), - ("GM2", "regen9ADBECBCD1C|2024-08-30--03-13-04--0"), + ("GM", "regen9ADBECBCD1C|2024-08-30--03-13-04--0"), ("NISSAN", "regen58464878D07|2024-08-30--03-15-31--0"), ("VOLKSWAGEN", "regenED976DEB757|2024-08-30--03-18-02--0"), ("MAZDA", "regenACF84CCF482|2024-08-30--03-21-55--0"), From 5d1816e2b8e783405458b2368770138030ef1baf Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 8 Apr 2025 21:27:05 -0700 Subject: [PATCH 008/477] Revert "test_models: update relay malfunction check" (#35002) Revert "test_models: update relay malfunction check (#34999)" This reverts commit 2cd0079abef0f654541dfc894e97b02829163577. --- opendbc_repo | 2 +- selfdrive/car/tests/test_models.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 5a24ffee45..81820502a0 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 5a24ffee4549b857837df03a7faaf5f31236f787 +Subproject commit 81820502a038b64e97caae81377ac65a3b86703e diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index fa266328f6..906fa99320 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -240,7 +240,6 @@ class TestCarModelBase(unittest.TestCase): continue to_send = libsafety_py.make_CANPacket(msg.address, msg.src % 4, msg.dat) - self.safety.safety_fwd_hook(msg.src, msg.address) if self.safety.safety_rx_hook(to_send) != 1: failed_addrs[hex(msg.address)] += 1 From f237649a7a281da851187eb3471a2c91f98974fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Wed, 9 Apr 2025 15:31:07 -0700 Subject: [PATCH 009/477] Reapply "Online lateral lag learning" (#34975) * Online lateral lag learning (#34974) This reverts commit b4cc9e68d126c2219a7ddf33519e222fe8bfe00d. * pad to the best size for fft * Fix static analysis * Add typing * Fix typing * MAX_LAG * Calculate cross correlation regardless if the points are valid * Back to lagd * Add lagd to process_config * Lagd in test onroad * Move lag estimator for lagd * Remove duplicate entry from test_onroad * Update process replay * pre-fill the data * Update cpu usage * 25sec window * Change the meaning of lateralDelayEstimate * No newline * Fix typing * Prefill * Update ref commit * Add a unit test * Fix static issues * Time limit * Or timeout * Use mocker * Update estimate every time * empty test * DT const * enable RIVIAN again * Update ref commit * Update that again * Improve the tests * Fix static * Add masking test * Increase timeout * Add liveDelay to selfdrived * Add liveDelay to selfdrived in process_replay * Fix block_avg restore after num_blocks * regen most * Update bolt * Update ref commit * Change the key name * Add assert * True weighted average --- cereal/services.py | 1 + common/params_keys.h | 1 + selfdrive/locationd/helpers.py | 38 ++ selfdrive/locationd/lagd.py | 333 ++++++++++++++++++ selfdrive/locationd/test/test_lagd.py | 137 +++++++ selfdrive/selfdrived/selfdrived.py | 2 +- .../test/process_replay/process_replay.py | 11 +- selfdrive/test/process_replay/ref_commit | 2 +- .../test/process_replay/test_processes.py | 34 +- selfdrive/test/test_onroad.py | 3 +- system/manager/process_config.py | 1 + 11 files changed, 542 insertions(+), 21 deletions(-) create mode 100755 selfdrive/locationd/lagd.py create mode 100644 selfdrive/locationd/test/test_lagd.py diff --git a/cereal/services.py b/cereal/services.py index aad83177bb..82fc04bd00 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -36,6 +36,7 @@ _services: dict[str, tuple] = { "errorLogMessage": (True, 0., 1), "liveCalibration": (True, 4., 4), "liveTorqueParameters": (True, 4., 1), + "liveDelay": (True, 4., 1), "androidLog": (True, 0.), "carState": (True, 100., 10), "carControl": (True, 100., 10), diff --git a/common/params_keys.h b/common/params_keys.h index fd701cda18..e630792357 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -71,6 +71,7 @@ inline static std::unordered_map keys = { {"LastPowerDropDetected", CLEAR_ON_MANAGER_START}, {"LastUpdateException", CLEAR_ON_MANAGER_START}, {"LastUpdateTime", PERSISTENT}, + {"LiveDelay", PERSISTENT}, {"LiveParameters", PERSISTENT}, {"LiveParametersV2", PERSISTENT}, {"LiveTorqueParameters", PERSISTENT | DONT_LOG}, diff --git a/selfdrive/locationd/helpers.py b/selfdrive/locationd/helpers.py index af307b0336..a3e3ae4a8c 100644 --- a/selfdrive/locationd/helpers.py +++ b/selfdrive/locationd/helpers.py @@ -1,10 +1,48 @@ import numpy as np from typing import Any +from functools import cache from cereal import log from openpilot.common.transformations.orientation import rot_from_euler, euler_from_rot +@cache +def fft_next_good_size(n: int) -> int: + """ + smallest composite of 2, 3, 5, 7, 11 that is >= n + inspired by pocketfft + """ + if n <= 6: + return n + best, f2 = 2 * n, 1 + while f2 < best: + f23 = f2 + while f23 < best: + f235 = f23 + while f235 < best: + f2357 = f235 + while f2357 < best: + f235711 = f2357 + while f235711 < best: + best = f235711 if f235711 >= n else best + f235711 *= 11 + f2357 *= 7 + f235 *= 5 + f23 *= 3 + f2 *= 2 + return best + + +def parabolic_peak_interp(R, max_index): + if max_index == 0 or max_index == len(R) - 1: + return max_index + + y_m1, y_0, y_p1 = R[max_index - 1], R[max_index], R[max_index + 1] + offset = 0.5 * (y_p1 - y_m1) / (2 * y_0 - y_p1 - y_m1) + + return max_index + offset + + def rotate_cov(rot_matrix, cov_in): return rot_matrix @ cov_in @ rot_matrix.T diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py new file mode 100755 index 0000000000..5ce82309ea --- /dev/null +++ b/selfdrive/locationd/lagd.py @@ -0,0 +1,333 @@ +#!/usr/bin/env python3 +import os +import numpy as np +import capnp +from collections import deque +from functools import partial + +import cereal.messaging as messaging +from cereal import car, log +from cereal.services import SERVICE_LIST +from openpilot.common.params import Params +from openpilot.common.realtime import config_realtime_process +from openpilot.common.swaglog import cloudlog +from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose, fft_next_good_size, parabolic_peak_interp + +BLOCK_SIZE = 100 +BLOCK_NUM = 50 +BLOCK_NUM_NEEDED = 5 +MOVING_WINDOW_SEC = 300.0 +MIN_OKAY_WINDOW_SEC = 25.0 +MIN_RECOVERY_BUFFER_SEC = 2.0 +MIN_VEGO = 15.0 +MIN_ABS_YAW_RATE = np.radians(1.0) +MIN_NCC = 0.95 +MAX_LAG = 1.0 + + +def masked_normalized_cross_correlation(expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, n: int): + """ + References: + D. Padfield. "Masked FFT registration". In Proc. Computer Vision and + Pattern Recognition, pp. 2918-2925 (2010). + :DOI:`10.1109/CVPR.2010.5540032` + """ + + eps = np.finfo(np.float64).eps + expected_sig = np.asarray(expected_sig, dtype=np.float64) + actual_sig = np.asarray(actual_sig, dtype=np.float64) + + expected_sig[~mask] = 0.0 + actual_sig[~mask] = 0.0 + + rotated_expected_sig = expected_sig[::-1] + rotated_mask = mask[::-1] + + fft = partial(np.fft.fft, n=n) + + actual_sig_fft = fft(actual_sig) + rotated_expected_sig_fft = fft(rotated_expected_sig) + actual_mask_fft = fft(mask.astype(np.float64)) + rotated_mask_fft = fft(rotated_mask.astype(np.float64)) + + number_overlap_masked_samples = np.fft.ifft(rotated_mask_fft * actual_mask_fft).real + number_overlap_masked_samples[:] = np.round(number_overlap_masked_samples) + number_overlap_masked_samples[:] = np.fmax(number_overlap_masked_samples, eps) + masked_correlated_actual_fft = np.fft.ifft(rotated_mask_fft * actual_sig_fft).real + masked_correlated_expected_fft = np.fft.ifft(actual_mask_fft * rotated_expected_sig_fft).real + + numerator = np.fft.ifft(rotated_expected_sig_fft * actual_sig_fft).real + numerator -= masked_correlated_actual_fft * masked_correlated_expected_fft / number_overlap_masked_samples + + actual_squared_fft = fft(actual_sig ** 2) + actual_sig_denom = np.fft.ifft(rotated_mask_fft * actual_squared_fft).real + actual_sig_denom -= masked_correlated_actual_fft ** 2 / number_overlap_masked_samples + actual_sig_denom[:] = np.fmax(actual_sig_denom, 0.0) + + rotated_expected_squared_fft = fft(rotated_expected_sig ** 2) + expected_sig_denom = np.fft.ifft(actual_mask_fft * rotated_expected_squared_fft).real + expected_sig_denom -= masked_correlated_expected_fft ** 2 / number_overlap_masked_samples + expected_sig_denom[:] = np.fmax(expected_sig_denom, 0.0) + + denom = np.sqrt(actual_sig_denom * expected_sig_denom) + + # zero-out samples with very small denominators + tol = 1e3 * eps * np.max(np.abs(denom), keepdims=True) + nonzero_indices = denom > tol + + ncc = np.zeros_like(denom, dtype=np.float64) + ncc[nonzero_indices] = numerator[nonzero_indices] / denom[nonzero_indices] + np.clip(ncc, -1, 1, out=ncc) + + return ncc + + +class Points: + def __init__(self, num_points: int): + self.times = deque[float]([0.0] * num_points, maxlen=num_points) + self.okay = deque[bool]([False] * num_points, maxlen=num_points) + self.desired = deque[float]([0.0] * num_points, maxlen=num_points) + self.actual = deque[float]([0.0] * num_points, maxlen=num_points) + + @property + def num_points(self): + return len(self.desired) + + @property + def num_okay(self): + return np.count_nonzero(self.okay) + + def update(self, t: float, desired: float, actual: float, okay: bool): + self.times.append(t) + self.okay.append(okay) + self.desired.append(desired) + self.actual.append(actual) + + def get(self) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + return np.array(self.times), np.array(self.desired), np.array(self.actual), np.array(self.okay) + + +class BlockAverage: + def __init__(self, num_blocks: int, block_size: int, valid_blocks: int, initial_value: float): + self.num_blocks = num_blocks + self.block_size = block_size + self.block_idx = valid_blocks % num_blocks + self.idx = 0 + + self.values = np.tile(initial_value, (num_blocks, 1)) + self.valid_blocks = valid_blocks + + def update(self, value: float): + self.values[self.block_idx] = (self.idx * self.values[self.block_idx] + value) / (self.idx + 1) + self.idx = (self.idx + 1) % self.block_size + if self.idx == 0: + self.block_idx = (self.block_idx + 1) % self.num_blocks + self.valid_blocks = min(self.valid_blocks + 1, self.num_blocks) + + def get(self) -> tuple[float, float]: + valid_block_idx = [i for i in range(self.valid_blocks) if i != self.block_idx] + valid_and_current_idx = valid_block_idx + ([self.block_idx] if self.idx > 0 else []) + + valid_mean = float(np.mean(self.values[valid_block_idx], axis=0).item()) if len(valid_block_idx) > 0 else float('nan') + current_mean = float(np.mean(self.values[valid_and_current_idx], axis=0).item()) if len(valid_and_current_idx) > 0 else float('nan') + return valid_mean, current_mean + + +class LateralLagEstimator: + inputs = {"carControl", "carState", "controlsState", "liveCalibration", "livePose"} + + def __init__(self, CP: car.CarParams, dt: float, + block_count: int = BLOCK_NUM, min_valid_block_count: int = BLOCK_NUM_NEEDED, block_size: int = BLOCK_SIZE, + window_sec: float = MOVING_WINDOW_SEC, okay_window_sec: float = MIN_OKAY_WINDOW_SEC, min_recovery_buffer_sec: float = MIN_RECOVERY_BUFFER_SEC, + min_vego: float = MIN_VEGO, min_yr: float = MIN_ABS_YAW_RATE, min_ncc: float = MIN_NCC): + self.dt = dt + self.window_sec = window_sec + self.okay_window_sec = okay_window_sec + self.min_recovery_buffer_sec = min_recovery_buffer_sec + self.initial_lag = CP.steerActuatorDelay + 0.2 + self.block_size = block_size + self.block_count = block_count + self.min_valid_block_count = min_valid_block_count + self.min_vego = min_vego + self.min_yr = min_yr + self.min_ncc = min_ncc + + self.t = 0.0 + self.lat_active = False + self.steering_pressed = False + self.steering_saturated = False + self.desired_curvature = 0.0 + self.v_ego = 0.0 + self.yaw_rate = 0.0 + + self.last_lat_inactive_t = 0.0 + self.last_steering_pressed_t = 0.0 + self.last_steering_saturated_t = 0.0 + self.last_estimate_t = 0.0 + + self.calibrator = PoseCalibrator() + + self.reset(self.initial_lag, 0) + + def reset(self, initial_lag: float, valid_blocks: int): + window_len = int(self.window_sec / self.dt) + self.points = Points(window_len) + self.block_avg = BlockAverage(self.block_count, self.block_size, valid_blocks, initial_lag) + + def get_msg(self, valid: bool, debug: bool = False) -> capnp._DynamicStructBuilder: + msg = messaging.new_message('liveDelay') + + msg.valid = valid + + liveDelay = msg.liveDelay + + valid_mean_lag, current_mean_lag = self.block_avg.get() + if self.block_avg.valid_blocks >= self.min_valid_block_count and not np.isnan(valid_mean_lag): + liveDelay.status = log.LiveDelayData.Status.estimated + liveDelay.lateralDelay = valid_mean_lag + else: + liveDelay.status = log.LiveDelayData.Status.unestimated + liveDelay.lateralDelay = self.initial_lag + if not np.isnan(current_mean_lag): + liveDelay.lateralDelayEstimate = current_mean_lag + else: + liveDelay.lateralDelayEstimate = self.initial_lag + liveDelay.validBlocks = self.block_avg.valid_blocks + if debug: + liveDelay.points = self.block_avg.values.flatten().tolist() + + return msg + + def handle_log(self, t: float, which: str, msg: capnp._DynamicStructReader): + if which == "carControl": + self.lat_active = msg.latActive + elif which == "carState": + self.steering_pressed = msg.steeringPressed + self.v_ego = msg.vEgo + elif which == "controlsState": + self.steering_saturated = getattr(msg.lateralControlState, msg.lateralControlState.which()).saturated + self.desired_curvature = msg.desiredCurvature + elif which == "liveCalibration": + self.calibrator.feed_live_calib(msg) + elif which == "livePose": + device_pose = Pose.from_live_pose(msg) + calibrated_pose = self.calibrator.build_calibrated_pose(device_pose) + self.yaw_rate = calibrated_pose.angular_velocity.z + self.t = t + + def points_enough(self): + return self.points.num_points >= int(self.okay_window_sec / self.dt) + + def points_valid(self): + return self.points.num_okay >= int(self.okay_window_sec / self.dt) + + def update_points(self): + if not self.lat_active: + self.last_lat_inactive_t = self.t + if self.steering_pressed: + self.last_steering_pressed_t = self.t + if self.steering_saturated: + self.last_steering_saturated_t = self.t + + la_desired = self.desired_curvature * self.v_ego * self.v_ego + la_actual_pose = self.yaw_rate * self.v_ego + + fast = self.v_ego > self.min_vego + turning = np.abs(self.yaw_rate) >= self.min_yr + has_recovered = all( # wait for recovery after !lat_active, steering_pressed, steering_saturated + self.t - last_t >= self.min_recovery_buffer_sec + for last_t in [self.last_lat_inactive_t, self.last_steering_pressed_t, self.last_steering_saturated_t] + ) + okay = self.lat_active and not self.steering_pressed and not self.steering_saturated and fast and turning and has_recovered + + self.points.update(self.t, la_desired, la_actual_pose, okay) + + def update_estimate(self): + if not self.points_enough(): + return + + times, desired, actual, okay = self.points.get() + # check if there are any new valid data points since the last update + is_valid = self.points_valid() + if self.last_estimate_t != 0 and times[0] <= self.last_estimate_t: + new_values_start_idx = next(-i for i, t in enumerate(reversed(times)) if t <= self.last_estimate_t) + is_valid = is_valid and not (new_values_start_idx == 0 or not np.any(okay[new_values_start_idx:])) + + delay, corr = self.actuator_delay(desired, actual, okay, self.dt, MAX_LAG) + if corr < self.min_ncc or not is_valid: + return + + self.block_avg.update(delay) + self.last_estimate_t = self.t + + def actuator_delay(self, expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, dt: float, max_lag: float) -> tuple[float, float]: + assert len(expected_sig) == len(actual_sig) + max_lag_samples = int(max_lag / dt) + padded_size = fft_next_good_size(len(expected_sig) + max_lag_samples) + + ncc = masked_normalized_cross_correlation(expected_sig, actual_sig, mask, padded_size) + + # only consider lags from 0 to max_lag + roi_ncc = ncc[len(expected_sig) - 1: len(expected_sig) - 1 + max_lag_samples] + + max_corr_index = np.argmax(roi_ncc) + corr = roi_ncc[max_corr_index] + lag = parabolic_peak_interp(roi_ncc, max_corr_index) * dt + + return lag, corr + + +def retrieve_initial_lag(params_reader: Params, CP: car.CarParams): + last_lag_data = params_reader.get("LiveDelay") + last_carparams_data = params_reader.get("CarParamsPrevRoute") + + if last_lag_data is not None: + try: + with log.Event.from_bytes(last_lag_data) as last_lag_msg, car.CarParams.from_bytes(last_carparams_data) as last_CP: + ld = last_lag_msg.liveDelay + if last_CP.carFingerprint != CP.carFingerprint: + raise Exception("Car model mismatch") + + lag, valid_blocks = ld.lateralDelayEstimate, ld.validBlocks + assert valid_blocks <= BLOCK_NUM, "Invalid number of valid blocks" + return lag, valid_blocks + except Exception as e: + cloudlog.error(f"Failed to retrieve initial lag: {e}") + + return None + + +def main(): + config_realtime_process([0, 1, 2, 3], 5) + + DEBUG = bool(int(os.getenv("DEBUG", "0"))) + + pm = messaging.PubMaster(['liveDelay']) + sm = messaging.SubMaster(['livePose', 'liveCalibration', 'carState', 'controlsState', 'carControl'], poll='livePose') + + params_reader = Params() + CP = messaging.log_from_bytes(params_reader.get("CarParams", block=True), car.CarParams) + + lag_learner = LateralLagEstimator(CP, 1. / SERVICE_LIST['livePose'].frequency) + if (initial_lag_params := retrieve_initial_lag(params_reader, CP)) is not None: + lag, valid_blocks = initial_lag_params + lag_learner.reset(lag, valid_blocks) + + while True: + sm.update() + if sm.all_checks(): + for which in sorted(sm.updated.keys(), key=lambda x: sm.logMonoTime[x]): + if sm.updated[which]: + t = sm.logMonoTime[which] * 1e-9 + lag_learner.handle_log(t, which, sm[which]) + lag_learner.update_points() + + # 4Hz driven by livePose + if sm.frame % 5 == 0: + lag_learner.update_estimate() + lag_msg = lag_learner.get_msg(sm.all_checks(), DEBUG) + lag_msg_dat = lag_msg.to_bytes() + pm.send('liveDelay', lag_msg_dat) + + if sm.frame % 1200 == 0: # cache every 60 seconds + params_reader.put_nonblocking("LiveDelay", lag_msg_dat) diff --git a/selfdrive/locationd/test/test_lagd.py b/selfdrive/locationd/test/test_lagd.py new file mode 100644 index 0000000000..53af2d2573 --- /dev/null +++ b/selfdrive/locationd/test/test_lagd.py @@ -0,0 +1,137 @@ +import random +import numpy as np +import time +import pytest + +from cereal import messaging +from openpilot.selfdrive.locationd.lagd import LateralLagEstimator, retrieve_initial_lag, masked_normalized_cross_correlation, \ + BLOCK_NUM_NEEDED, BLOCK_SIZE, MIN_OKAY_WINDOW_SEC +from openpilot.selfdrive.test.process_replay.migration import migrate, migrate_carParams +from openpilot.selfdrive.locationd.test.test_locationd_scenarios import TEST_ROUTE +from openpilot.common.params import Params +from openpilot.tools.lib.logreader import LogReader +from openpilot.system.hardware import PC + +MAX_ERR_FRAMES = 1 +DT = 0.05 + + +def process_messages(mocker, estimator, lag_frames, n_frames, vego=20.0, rejection_threshold=0.0): + class ZeroMock(mocker.Mock): + def __getattr__(self, *args): + return 0 + + for i in range(n_frames): + t = i * estimator.dt + desired_la = np.cos(t) + actual_la = np.cos(t - lag_frames * estimator.dt) + + # if sample is masked out, set it to desired value (no lag) + rejected = random.uniform(0, 1) < rejection_threshold + if rejected: + actual_la = desired_la + + desired_cuvature = desired_la / (vego ** 2) + actual_yr = actual_la / vego + msgs = [ + (t, "carControl", mocker.Mock(latActive=not rejected)), + (t, "carState", mocker.Mock(vEgo=vego, steeringPressed=False)), + (t, "controlsState", mocker.Mock(desiredCurvature=desired_cuvature, + lateralControlState=mocker.Mock(which=mocker.Mock(return_value='debugControlState'), debugControlState=ZeroMock()))), + (t, "livePose", mocker.Mock(orientationNED=ZeroMock(), + velocityDevice=ZeroMock(), + accelerationDevice=ZeroMock(), + angularVelocityDevice=ZeroMock(z=actual_yr))), + ] + for t, w, m in msgs: + estimator.handle_log(t, w, m) + estimator.update_points() + estimator.update_estimate() + + +class TestLagd: + def test_read_saved_params(self): + params = Params() + + lr = migrate(LogReader(TEST_ROUTE), [migrate_carParams]) + CP = next(m for m in lr if m.which() == "carParams").carParams + + msg = messaging.new_message('liveDelay') + msg.liveDelay.lateralDelayEstimate = random.random() + msg.liveDelay.validBlocks = random.randint(1, 10) + params.put("LiveDelay", msg.to_bytes()) + params.put("CarParamsPrevRoute", CP.as_builder().to_bytes()) + + saved_lag_params = retrieve_initial_lag(params, CP) + assert saved_lag_params is not None + + lag, valid_blocks = saved_lag_params + assert lag == msg.liveDelay.lateralDelayEstimate + assert valid_blocks == msg.liveDelay.validBlocks + + def test_ncc(self): + lag_frames = random.randint(1, 19) + + desired_sig = np.sin(np.arange(0.0, 10.0, 0.1)) + actual_sig = np.sin(np.arange(0.0, 10.0, 0.1) - lag_frames * 0.1) + mask = np.ones(len(desired_sig), dtype=bool) + + corr = masked_normalized_cross_correlation(desired_sig, actual_sig, mask, 200)[len(desired_sig) - 1:len(desired_sig) + 20] + assert np.argmax(corr) == lag_frames + + # add some noise + desired_sig += np.random.normal(0, 0.05, len(desired_sig)) + actual_sig += np.random.normal(0, 0.05, len(actual_sig)) + corr = masked_normalized_cross_correlation(desired_sig, actual_sig, mask, 200)[len(desired_sig) - 1:len(desired_sig) + 20] + assert np.argmax(corr) in range(lag_frames - MAX_ERR_FRAMES, lag_frames + MAX_ERR_FRAMES + 1) + + # mask out 40% of the values, and make them noise + mask = np.random.choice([True, False], size=len(desired_sig), p=[0.6, 0.4]) + desired_sig[~mask] = np.random.normal(0, 1, size=np.sum(~mask)) + actual_sig[~mask] = np.random.normal(0, 1, size=np.sum(~mask)) + corr = masked_normalized_cross_correlation(desired_sig, actual_sig, mask, 200)[len(desired_sig) - 1:len(desired_sig) + 20] + assert np.argmax(corr) in range(lag_frames - MAX_ERR_FRAMES, lag_frames + MAX_ERR_FRAMES + 1) + + def test_empty_estimator(self, mocker): + mocked_CP = mocker.Mock(steerActuatorDelay=0.8) + estimator = LateralLagEstimator(mocked_CP, DT) + msg = estimator.get_msg(True) + assert msg.liveDelay.status == 'unestimated' + assert np.allclose(msg.liveDelay.lateralDelay, estimator.initial_lag) + assert np.allclose(msg.liveDelay.lateralDelayEstimate, estimator.initial_lag) + assert msg.liveDelay.validBlocks == 0 + + def test_estimator_basics(self, mocker, subtests): + for lag_frames in range(5): + with subtests.test(msg=f"lag_frames={lag_frames}"): + mocked_CP = mocker.Mock(steerActuatorDelay=0.8) + estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0) + process_messages(mocker, estimator, lag_frames, int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_NUM_NEEDED * BLOCK_SIZE) + msg = estimator.get_msg(True) + assert msg.liveDelay.status == 'estimated' + assert np.allclose(msg.liveDelay.lateralDelay, lag_frames * DT, atol=0.01) + assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01) + assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED + + def test_estimator_masking(self, mocker): + mocked_CP, lag_frames = mocker.Mock(steerActuatorDelay=0.8), random.randint(1, 19) + estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0, min_valid_block_count=1) + process_messages(mocker, estimator, lag_frames, (int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_SIZE) * 2, rejection_threshold=0.4) + msg = estimator.get_msg(True) + assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01) + + @pytest.mark.skipif(PC, reason="only on device") + @pytest.mark.timeout(60) + def test_estimator_performance(self, mocker): + mocked_CP = mocker.Mock(steerActuatorDelay=0.8) + estimator = LateralLagEstimator(mocked_CP, DT) + + ds = [] + for _ in range(1000): + st = time.perf_counter() + estimator.update_points() + estimator.update_estimate() + d = time.perf_counter() - st + ds.append(d) + + assert np.mean(ds) < DT diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index b1399ab43b..c2e8259135 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -75,7 +75,7 @@ class SelfdriveD: # no vipc in replay will make them ignored anyways ignore += ['roadCameraState', 'wideRoadCameraState'] self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', - 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', + 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'controlsState', 'carControl', 'driverAssistance', 'alertDebug'] + \ self.camera_packets + self.sensor_packets + self.gps_packets, diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 3ee761ee44..9715e48615 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -462,7 +462,7 @@ CONFIGS = [ proc_name="selfdrived", pubs=[ "carState", "deviceState", "pandaStates", "peripheralState", "liveCalibration", "driverMonitoringState", - "longitudinalPlan", "livePose", "liveParameters", "radarState", + "longitudinalPlan", "livePose", "liveDelay", "liveParameters", "radarState", "modelV2", "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState", "liveTorqueParameters", "accelerometer", "gyroscope", "carOutput", "gpsLocationExternal", "gpsLocation", "controlsState", "carControl", "driverAssistance", "alertDebug", @@ -551,6 +551,15 @@ CONFIGS = [ tolerance=NUMPY_TOLERANCE, processing_time=0.004, ), + ProcessConfig( + proc_name="lagd", + pubs=["livePose", "liveCalibration", "carState", "carControl", "controlsState"], + subs=["liveDelay"], + ignore=["logMonoTime"], + init_callback=get_car_params_callback, + should_recv_callback=MessageBasedRcvCallback("livePose"), + tolerance=NUMPY_TOLERANCE, + ), ProcessConfig( proc_name="ubloxd", pubs=["ubloxRaw"], diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index d4b02296d6..d7eb3686db 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -37fd7afb99bf188c1e5375d01f011ae35821f640 \ No newline at end of file +4576c437dc14bc830956dd272dade4d7f027dab5 \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index bb30ab2acd..d479369693 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -41,23 +41,23 @@ source_segments = [ ] segments = [ - ("BODY", "regenA67A128BCD8|2024-08-30--02-36-22--0"), - ("HYUNDAI", "regenCCD47FEBC0C|2025-03-04--03-21-48--0"), - ("HYUNDAI2", "regen306779F6870|2024-10-03--04-03-23--0"), - ("TOYOTA", "regen4A5115B248D|2025-03-04--03-21-43--0"), - ("TOYOTA2", "regen6E484EDAB96|2024-08-30--02-47-37--0"), - ("TOYOTA3", "8011d605be1cbb77|000000cc--8e8d8ec716--6"), - ("HONDA", "regenB8CABEC09CC|2025-03-04--03-32-55--0"), - ("HONDA2", "regen4B38A7428CD|2024-08-30--02-56-31--0"), - ("CHRYSLER", "regenF3DBBA9E8DF|2024-08-30--02-59-03--0"), - ("RAM", "regenDB02684E00A|2024-08-30--03-02-54--0"), - ("SUBARU", "regen5E3347D0A0F|2025-03-04--03-23-55--0"), - ("GM", "regen9ADBECBCD1C|2024-08-30--03-13-04--0"), - ("NISSAN", "regen58464878D07|2024-08-30--03-15-31--0"), - ("VOLKSWAGEN", "regenED976DEB757|2024-08-30--03-18-02--0"), + ("BODY", "regen2F3C7259F1B|2025-04-08--23-00-23--0"), + ("HYUNDAI", "regenAA0FC4ED71E|2025-04-08--22-57-50--0"), + ("HYUNDAI2", "regenAFB9780D823|2025-04-08--23-00-34--0"), + ("TOYOTA", "regen218A4DCFAA1|2025-04-08--22-57-51--0"), + ("TOYOTA2", "regen107352E20EB|2025-04-08--22-57-46--0"), + ("TOYOTA3", "regen1455E3B4BDF|2025-04-09--03-26-06--0"), + ("HONDA", "regenB328FF8BA0A|2025-04-08--22-57-45--0"), + ("HONDA2", "regen6170C8C9A35|2025-04-08--22-57-46--0"), + ("CHRYSLER", "regen5B28FC2A437|2025-04-08--23-04-24--0"), + ("RAM", "regenBF81EA96E08|2025-04-08--23-06-54--0"), + ("SUBARU", "regen7366F13F6A1|2025-04-08--23-07-07--0"), + ("GM", "regen1271097D038|2025-04-09--03-26-00--0"), + ("NISSAN", "regen15D60604EAB|2025-04-08--23-06-59--0"), + ("VOLKSWAGEN", "regen0F2F06C9539|2025-04-08--23-06-56--0"), ("MAZDA", "regenACF84CCF482|2024-08-30--03-21-55--0"), - ("FORD", "regenA75209BD115|2025-03-04--03-23-53--0"), - ("RIVIAN", "bc095dc92e101734|000000db--ee9fe46e57--1"), + ("FORD", "regen755D8CB1E1F|2025-04-08--23-13-43--0"), + ("RIVIAN", "regen5FCAC896BBE|2025-04-08--23-13-35--0"), ] # dashcamOnly makes don't need to be tested until a full port is done @@ -195,7 +195,7 @@ if __name__ == "__main__": continue # to speed things up, we only test all segments on card - if cfg.proc_name != 'card' and car_brand not in ('HYUNDAI', 'TOYOTA', 'HONDA', 'SUBARU', 'FORD'): + if cfg.proc_name != 'card' and car_brand not in ('HYUNDAI', 'TOYOTA', 'HONDA', 'SUBARU', 'FORD', 'RIVIAN'): continue cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{cur_commit}.zst") diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index ebc87800b7..4cd952219c 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -44,7 +44,6 @@ PROCS = { "./camerad": 10.0, "selfdrive.controls.plannerd": 9.0, "./ui": 18.0, - "selfdrive.locationd.paramsd": 9.0, "./sensord": 7.0, "selfdrive.controls.radard": 2.0, "selfdrive.modeld.modeld": 22.0, @@ -53,6 +52,8 @@ PROCS = { "selfdrive.locationd.calibrationd": 2.0, "selfdrive.locationd.torqued": 5.0, "selfdrive.locationd.locationd": 25.0, + "selfdrive.locationd.paramsd": 9.0, + "selfdrive.locationd.lagd": 11.0, "selfdrive.ui.soundd": 3.0, "selfdrive.monitoring.dmonitoringd": 4.0, "./proclogd": 2.0, diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 6e048c339e..e25d556037 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -94,6 +94,7 @@ procs = [ PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), PythonProcess("pandad", "selfdrive.pandad.pandad", always_run), PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad), + PythonProcess("lagd", "selfdrive.locationd.lagd", only_onroad), NativeProcess("ubloxd", "system/ubloxd", ["./ubloxd"], ublox, enabled=TICI), PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI), PythonProcess("plannerd", "selfdrive.controls.plannerd", not_long_maneuver), From 3e6267967b21f2339c98df996644db82765be8d5 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Thu, 10 Apr 2025 16:33:36 +0200 Subject: [PATCH 010/477] Raise thermal setpoint (#35008) We don't need to control all the way down to 70C --- system/hardware/fan_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/hardware/fan_controller.py b/system/hardware/fan_controller.py index be93d5922a..7d5bec0509 100755 --- a/system/hardware/fan_controller.py +++ b/system/hardware/fan_controller.py @@ -27,7 +27,7 @@ class TiciFanController(BaseFanController): if ignition != self.last_ignition: self.controller.reset() - error = 70 - cur_temp + error = 75 - cur_temp fan_pwr_out = -int(self.controller.update( error=error, feedforward=np.interp(cur_temp, [60.0, 100.0], [0, -100]) From 24c2d9a68a03f3282519c7ffd82241e38df305f5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 10 Apr 2025 15:57:15 -0700 Subject: [PATCH 011/477] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 8a583aaa8c..0dc95a89dd 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 8a583aaa8c1a27a3d13747b7ecead6a3d73a4e81 +Subproject commit 0dc95a89dd7df1e27c36d7560c39666151af81df From b0915db50a464c55803b6bb7695a6fb45129540e Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:55:12 -0700 Subject: [PATCH 012/477] [bot] Update Python packages (#35012) * Update Python packages * update refs --------- Co-authored-by: Vehicle Researcher Co-authored-by: Shane Smiskol --- docs/CARS.md | 4 +- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- uv.lock | 138 ++++++++++++----------- 4 files changed, 76 insertions(+), 70 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 1af8974f30..a1b027c51b 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 302 Supported Cars +# 304 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -71,6 +71,8 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Civic Hatchback 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic Hatchback Hybrid 2023 (Europe only)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic Hatchback Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/opendbc_repo b/opendbc_repo index 81820502a0..00e7a69382 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 81820502a038b64e97caae81377ac65a3b86703e +Subproject commit 00e7a69382d37694ab585466c187c814f7de4c56 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index d7eb3686db..632922a8a4 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -4576c437dc14bc830956dd272dade4d7f027dab5 \ No newline at end of file +3d3f875dc211ab0ca8a8e564ecf6dd3f52fcf7d9 \ No newline at end of file diff --git a/uv.lock b/uv.lock index aa49b605a5..7141fa5195 100644 --- a/uv.lock +++ b/uv.lock @@ -70,15 +70,15 @@ wheels = [ [[package]] name = "aioice" -version = "0.9.0" +version = "0.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dnspython" }, { name = "ifaddr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/b6/e2b0e48ccb5b04fe29265e93f14a0915f416e359c897ae87d570566c430b/aioice-0.9.0.tar.gz", hash = "sha256:fc2401b1c4b6e19372eaaeaa28fd1bd9cbf6b0e412e48625297c53b495eebd1e", size = 40324 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/8e/a68b686678c6c21e9d5a247d7b2849fd55e645179ebcd081889b53115854/aioice-0.10.0.tar.gz", hash = "sha256:25dfdcbacb89de0e2eac41e96a872499c37a9eb74fa22a0e92d8ac1a0a9141af", size = 44236 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/35/d21e48d3ba25d32aba5d142d54c4491376c659dd74d052a30dd25198007b/aioice-0.9.0-py3-none-any.whl", hash = "sha256:b609597a3a5a611e0004ff04772e16aceb881d51c25c0afc4ceac05d5e50024e", size = 24177 }, + { url = "https://files.pythonhosted.org/packages/77/07/45f3f2864a8a5c1e0bf8618802dcc1d85f901cc13506a1d9eb93d1d2cbcb/aioice-0.10.0-py3-none-any.whl", hash = "sha256:e8c91083cc7858471554370d9037118d66582e25be24f4c6ad9ced2c2ba44bbe", size = 24882 }, ] [[package]] @@ -877,11 +877,11 @@ wheels = [ [[package]] name = "markdown" -version = "3.7" +version = "3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, + { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210 }, ] [[package]] @@ -1092,41 +1092,45 @@ wheels = [ [[package]] name = "multidict" -version = "6.3.2" +version = "6.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/2d/6e0d6771cadd5ad14d13193cc8326dc0b341cc1659c306cbfce7a5058fff/multidict-6.3.2.tar.gz", hash = "sha256:c1035eea471f759fa853dd6e76aaa1e389f93b3e1403093fa0fd3ab4db490678", size = 88060 } +sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/e3/443e682e42eaddad0b217b7a59627927fa42b6cd7ba7174f0a01eb3fe6b8/multidict-6.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a034f41fcd16968c0470d8912d293d7b0d0822fc25739c5c2ff7835b85bc56", size = 62734 }, - { url = "https://files.pythonhosted.org/packages/b1/4f/2126e9bc37f5be2fdfa36cc192e7ef10b3e9c58eec75a4468706aca96891/multidict-6.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:352585cec45f5d83d886fc522955492bb436fca032b11d487b12d31c5a81b9e3", size = 37115 }, - { url = "https://files.pythonhosted.org/packages/6a/af/5aae0c05a66fdf8bf015ee6903d3a250a7d9c6cc75c9478d04995e6ff1e2/multidict-6.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:da9d89d293511fd0a83a90559dc131f8b3292b6975eb80feff19e5f4663647e2", size = 36371 }, - { url = "https://files.pythonhosted.org/packages/94/27/42390b75c20ff63f43fce44f36f9f66be466cd9ee05326051e4caacdb75b/multidict-6.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fa716592224aa652b9347a586cfe018635229074565663894eb4eb21f8307f", size = 243444 }, - { url = "https://files.pythonhosted.org/packages/21/55/77077af851d7678fe0845c4050a537321d82fb12a04d4f6db334a1cc6ff7/multidict-6.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0326278a44c56e94792475268e5cd3d47fbc0bd41ee56928c3bbb103ba7f58fe", size = 256750 }, - { url = "https://files.pythonhosted.org/packages/f1/09/4c5bfeb2fc8a1e14002239bd6a4d9ba2963fb148889d444b05a20db32a41/multidict-6.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb1ea87f7fe45e5079f6315e95d64d4ca8b43ef656d98bed63a02e3756853a22", size = 251630 }, - { url = "https://files.pythonhosted.org/packages/24/a9/286756a1afb8648772de851f8f39d2dd4076506f0c0fc2b751259fcbf0dd/multidict-6.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cff3c5a98d037024a9065aafc621a8599fad7b423393685dc83cf7a32f8b691", size = 238522 }, - { url = "https://files.pythonhosted.org/packages/c2/03/4bb17df70742aae786fcbc27e89e2e49c322134698cd0739aec93e91c669/multidict-6.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed99834b053c655d980fb98029003cb24281e47a796052faad4543aa9e01b8e8", size = 230230 }, - { url = "https://files.pythonhosted.org/packages/53/cc/30df95ba07a9f233ae48d0605b3f72457364836b61a8a8e3d333fdcd32c0/multidict-6.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7048440e505d2b4741e5d0b32bd2f427c901f38c7760fc245918be2cf69b3b85", size = 239676 }, - { url = "https://files.pythonhosted.org/packages/25/37/2d9fe2944c2df5b71ba90cf657b90ad65f1542989cdabe4d1bdbf8c51530/multidict-6.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27248c27b563f5889556da8a96e18e98a56ff807ac1a7d56cf4453c2c9e4cd91", size = 238143 }, - { url = "https://files.pythonhosted.org/packages/ce/13/8f833f9f992eae49f4cb1a1ad05b8fbe183721a154d51c2136b177a41bdb/multidict-6.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6323b4ba0e018bd266f776c35f3f0943fc4ee77e481593c9f93bd49888f24e94", size = 248817 }, - { url = "https://files.pythonhosted.org/packages/15/d4/4f49c41af6c4cab962ad51436e6c5acfbdab4fa54f5e98faa56f66f89b03/multidict-6.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:81f7ce5ec7c27d0b45c10449c8f0fed192b93251e2e98cb0b21fec779ef1dc4d", size = 241268 }, - { url = "https://files.pythonhosted.org/packages/af/60/e723a00f7bb44366eab8d02fe6f076ecfad58331e10f6f0ce94cb989819c/multidict-6.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03bfcf2825b3bed0ba08a9d854acd18b938cab0d2dba3372b51c78e496bac811", size = 238267 }, - { url = "https://files.pythonhosted.org/packages/62/a6/f6b63fc51c8a4e228e6d2105061be3048b02d490d47e67f7ec2de575f1d0/multidict-6.3.2-cp311-cp311-win32.whl", hash = "sha256:f32c2790512cae6ca886920e58cdc8c784bdc4bb2a5ec74127c71980369d18dc", size = 34986 }, - { url = "https://files.pythonhosted.org/packages/85/56/ea976a5e3ebe0e871e004d9cacfe4c803f8ade353eaf4a247580e9dd7b9d/multidict-6.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:0b0c15e58e038a2cd75ef7cf7e072bc39b5e0488b165902efb27978984bbad70", size = 38427 }, - { url = "https://files.pythonhosted.org/packages/83/ae/bd7518193b4374484c04ba0f6522d0572dc17fcd53d238deb3cb3643c858/multidict-6.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d1e0ba1ce1b8cc79117196642d95f4365e118eaf5fb85f57cdbcc5a25640b2a4", size = 62680 }, - { url = "https://files.pythonhosted.org/packages/59/e0/a0a9247c32f385ac4c1afefe9c3f2271fb8e235aad72332d42384c41b9cb/multidict-6.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:029bbd7d782251a78975214b78ee632672310f9233d49531fc93e8e99154af25", size = 37366 }, - { url = "https://files.pythonhosted.org/packages/c3/fa/8c23cdd4492d59bea0e762662285f2163766e69e5ea715fe6a03a8670660/multidict-6.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d7db41e3b56817d9175264e5fe00192fbcb8e1265307a59f53dede86161b150e", size = 36103 }, - { url = "https://files.pythonhosted.org/packages/87/35/3bcc3616cb54d3a327b1d26dbec284c3eb7b179e8a78a6075852dbb51dac/multidict-6.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcab18e65cc555ac29981a581518c23311f2b1e72d8f658f9891590465383be", size = 248231 }, - { url = "https://files.pythonhosted.org/packages/b8/c3/17ddbfd6fc3eed9ab7326a43651e1a97da73f7acc69b78a7bb04b59c073d/multidict-6.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d50eff89aa4d145a5486b171a2177042d08ea5105f813027eb1050abe91839f", size = 259423 }, - { url = "https://files.pythonhosted.org/packages/1f/67/64b18180e8f559cc93efaaaac2fe0746b9c978560866b6fdd626d3237129/multidict-6.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:643e57b403d3e240045a3681f9e6a04d35a33eddc501b4cbbbdbc9c70122e7bc", size = 256204 }, - { url = "https://files.pythonhosted.org/packages/21/f6/e81a8e4817c2d32787b33ae58c72dc3fe08e0ba8e56e660a225df3cb8619/multidict-6.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d17b37b9715b30605b5bab1460569742d0c309e5c20079263b440f5d7746e7e", size = 249663 }, - { url = "https://files.pythonhosted.org/packages/3e/e8/44ca66758df031a8119483cf5385e2ff3b09b9c6df8f3396d626c325b553/multidict-6.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68acd51fa94e63312b8ddf84bfc9c3d3442fe1f9988bbe1b6c703043af8867fe", size = 232236 }, - { url = "https://files.pythonhosted.org/packages/93/76/d2faabbac582dc100a4d7ecf7d0ab8dd2aadf7f10d5d5a19e9932cf63a2e/multidict-6.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:347eea2852ab7f697cc5ed9b1aae96b08f8529cca0c6468f747f0781b1842898", size = 252638 }, - { url = "https://files.pythonhosted.org/packages/63/37/f5a6ea10dab96491b7300be940f86a5490dc474d18473c438f2550b78da3/multidict-6.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4d3f8e57027dcda84a1aa181501c15c45eab9566eb6fcc274cbd1e7561224f8", size = 247917 }, - { url = "https://files.pythonhosted.org/packages/d4/b1/2c32b684763b69becbaaa61b7af8a45a6f757fc82d9b4b123ca90cb69f75/multidict-6.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9ca57a841ffcf712e47875d026aa49d6e67f9560624d54b51628603700d5d287", size = 261754 }, - { url = "https://files.pythonhosted.org/packages/cd/f2/badedad94e1731debe56d076c9e61a1658c5e9d65dfa9c1ee74d1e3d31d7/multidict-6.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7cafdafb44c4e646118410368307693e49d19167e5f119cbe3a88697d2d1a636", size = 256389 }, - { url = "https://files.pythonhosted.org/packages/c6/3a/0a3488be2e5a6499f512e748d31e8fb90b753eb35793ecf390b9d8548e66/multidict-6.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:430120c6ce3715a9c6075cabcee557daccbcca8ba25a9fedf05c7bf564532f2d", size = 251902 }, - { url = "https://files.pythonhosted.org/packages/fe/44/62f76d0a5d836b96168f39a402a75dd3114d0df3cbb5669e0310034b71be/multidict-6.3.2-cp312-cp312-win32.whl", hash = "sha256:13bec31375235a68457ab887ce1bbf4f59d5810d838ae5d7e5b416242e1f3ed4", size = 35101 }, - { url = "https://files.pythonhosted.org/packages/8f/a4/7aaf2313e1766710010c35f9d738fd6309fb71a758f8c0e81853b90afb3d/multidict-6.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:c3b6d7620e6e90c6d97eaf3a63bf7fbd2ba253aab89120a4a9c660bf2d675391", size = 38479 }, - { url = "https://files.pythonhosted.org/packages/aa/c1/7832c95a50641148b567b5366dd3354489950dcfd01c8fc28472bec63b9a/multidict-6.3.2-py3-none-any.whl", hash = "sha256:71409d4579f716217f23be2f5e7afca5ca926aaeb398aa11b72d793bff637a1f", size = 10347 }, + { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259 }, + { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451 }, + { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706 }, + { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669 }, + { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182 }, + { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025 }, + { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481 }, + { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492 }, + { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279 }, + { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733 }, + { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089 }, + { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257 }, + { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728 }, + { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087 }, + { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137 }, + { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959 }, + { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541 }, + { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019 }, + { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925 }, + { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008 }, + { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374 }, + { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869 }, + { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949 }, + { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032 }, + { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517 }, + { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291 }, + { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982 }, + { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823 }, + { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714 }, + { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739 }, + { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809 }, + { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934 }, + { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242 }, + { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635 }, + { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400 }, ] [[package]] @@ -4387,14 +4391,14 @@ wheels = [ [[package]] name = "pytest-repeat" -version = "0.9.3" +version = "0.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/5e/99365eb229efff0b1bd475886150fc6db9937ab7e1bd21f6f65c1279e0eb/pytest_repeat-0.9.3.tar.gz", hash = "sha256:ffd3836dfcd67bb270bec648b330e20be37d2966448c4148c4092d1e8aba8185", size = 6272 } +sdist = { url = "https://files.pythonhosted.org/packages/80/d4/69e9dbb9b8266df0b157c72be32083403c412990af15c7c15f7a3fd1b142/pytest_repeat-0.9.4.tar.gz", hash = "sha256:d92ac14dfaa6ffcfe6917e5d16f0c9bc82380c135b03c2a5f412d2637f224485", size = 6488 } wheels = [ - { url = "https://files.pythonhosted.org/packages/49/a8/0a0aec0c2541b8baf4a0b95af2ba99abce217ee43534adf9cb7c908cf184/pytest_repeat-0.9.3-py3-none-any.whl", hash = "sha256:26ab2df18226af9d5ce441c858f273121e92ff55f5bb311d25755b8d7abdd8ed", size = 4196 }, + { url = "https://files.pythonhosted.org/packages/73/d4/8b706b81b07b43081bd68a2c0359fe895b74bf664b20aca8005d2bb3be71/pytest_repeat-0.9.4-py3-none-any.whl", hash = "sha256:c1738b4e412a6f3b3b9e0b8b29fcd7a423e50f87381ad9307ef6f5a8601139f3", size = 4180 }, ] [[package]] @@ -4713,27 +4717,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.11.4" +version = "0.11.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/5b/3ae20f89777115944e89c2d8c2e795dcc5b9e04052f76d5347e35e0da66e/ruff-0.11.4.tar.gz", hash = "sha256:f45bd2fb1a56a5a85fae3b95add03fb185a0b30cf47f5edc92aa0355ca1d7407", size = 3933063 } +sdist = { url = "https://files.pythonhosted.org/packages/45/71/5759b2a6b2279bb77fe15b1435b89473631c2cd6374d45ccdb6b785810be/ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef", size = 3976488 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/db/baee59ac88f57527fcbaad3a7b309994e42329c6bc4d4d2b681a3d7b5426/ruff-0.11.4-py3-none-linux_armv6l.whl", hash = "sha256:d9f4a761ecbde448a2d3e12fb398647c7f0bf526dbc354a643ec505965824ed2", size = 10106493 }, - { url = "https://files.pythonhosted.org/packages/c1/d6/9a0962cbb347f4ff98b33d699bf1193ff04ca93bed4b4222fd881b502154/ruff-0.11.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8c1747d903447d45ca3d40c794d1a56458c51e5cc1bc77b7b64bd2cf0b1626cc", size = 10876382 }, - { url = "https://files.pythonhosted.org/packages/3a/8f/62bab0c7d7e1ae3707b69b157701b41c1ccab8f83e8501734d12ea8a839f/ruff-0.11.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:51a6494209cacca79e121e9b244dc30d3414dac8cc5afb93f852173a2ecfc906", size = 10237050 }, - { url = "https://files.pythonhosted.org/packages/09/96/e296965ae9705af19c265d4d441958ed65c0c58fc4ec340c27cc9d2a1f5b/ruff-0.11.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f171605f65f4fc49c87f41b456e882cd0c89e4ac9d58e149a2b07930e1d466f", size = 10424984 }, - { url = "https://files.pythonhosted.org/packages/e5/56/644595eb57d855afed6e54b852e2df8cd5ca94c78043b2f29bdfb29882d5/ruff-0.11.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebf99ea9af918878e6ce42098981fc8c1db3850fef2f1ada69fb1dcdb0f8e79e", size = 9957438 }, - { url = "https://files.pythonhosted.org/packages/86/83/9d3f3bed0118aef3e871ded9e5687fb8c5776bde233427fd9ce0a45db2d4/ruff-0.11.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edad2eac42279df12e176564a23fc6f4aaeeb09abba840627780b1bb11a9d223", size = 11547282 }, - { url = "https://files.pythonhosted.org/packages/40/e6/0c6e4f5ae72fac5ccb44d72c0111f294a5c2c8cc5024afcb38e6bda5f4b3/ruff-0.11.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f103a848be9ff379fc19b5d656c1f911d0a0b4e3e0424f9532ececf319a4296e", size = 12182020 }, - { url = "https://files.pythonhosted.org/packages/b5/92/4aed0e460aeb1df5ea0c2fbe8d04f9725cccdb25d8da09a0d3f5b8764bf8/ruff-0.11.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:193e6fac6eb60cc97b9f728e953c21cc38a20077ed64f912e9d62b97487f3f2d", size = 11679154 }, - { url = "https://files.pythonhosted.org/packages/1b/d3/7316aa2609f2c592038e2543483eafbc62a0e1a6a6965178e284808c095c/ruff-0.11.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7af4e5f69b7c138be8dcffa5b4a061bf6ba6a3301f632a6bce25d45daff9bc99", size = 13905985 }, - { url = "https://files.pythonhosted.org/packages/63/80/734d3d17546e47ff99871f44ea7540ad2bbd7a480ed197fe8a1c8a261075/ruff-0.11.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:126b1bf13154aa18ae2d6c3c5efe144ec14b97c60844cfa6eb960c2a05188222", size = 11348343 }, - { url = "https://files.pythonhosted.org/packages/04/7b/70fc7f09a0161dce9613a4671d198f609e653d6f4ff9eee14d64c4c240fb/ruff-0.11.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8806daaf9dfa881a0ed603f8a0e364e4f11b6ed461b56cae2b1c0cab0645304", size = 10308487 }, - { url = "https://files.pythonhosted.org/packages/1a/22/1cdd62dabd678d75842bf4944fd889cf794dc9e58c18cc547f9eb28f95ed/ruff-0.11.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5d94bb1cc2fc94a769b0eb975344f1b1f3d294da1da9ddbb5a77665feb3a3019", size = 9929091 }, - { url = "https://files.pythonhosted.org/packages/9f/20/40e0563506332313148e783bbc1e4276d657962cc370657b2fff20e6e058/ruff-0.11.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:995071203d0fe2183fc7a268766fd7603afb9996785f086b0d76edee8755c896", size = 10924659 }, - { url = "https://files.pythonhosted.org/packages/b5/41/eef9b7aac8819d9e942f617f9db296f13d2c4576806d604aba8db5a753f1/ruff-0.11.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a37ca937e307ea18156e775a6ac6e02f34b99e8c23fe63c1996185a4efe0751", size = 11428160 }, - { url = "https://files.pythonhosted.org/packages/ff/61/c488943414fb2b8754c02f3879de003e26efdd20f38167ded3fb3fc1cda3/ruff-0.11.4-py3-none-win32.whl", hash = "sha256:0e9365a7dff9b93af933dab8aebce53b72d8f815e131796268709890b4a83270", size = 10311496 }, - { url = "https://files.pythonhosted.org/packages/b6/2b/2a1c8deb5f5dfa3871eb7daa41492c4d2b2824a74d2b38e788617612a66d/ruff-0.11.4-py3-none-win_amd64.whl", hash = "sha256:5a9fa1c69c7815e39fcfb3646bbfd7f528fa8e2d4bebdcf4c2bd0fa037a255fb", size = 11399146 }, - { url = "https://files.pythonhosted.org/packages/4f/03/3aec4846226d54a37822e4c7ea39489e4abd6f88388fba74e3d4abe77300/ruff-0.11.4-py3-none-win_arm64.whl", hash = "sha256:d435db6b9b93d02934cf61ef332e66af82da6d8c69aefdea5994c89997c7a0fc", size = 10450306 }, + { url = "https://files.pythonhosted.org/packages/23/db/6efda6381778eec7f35875b5cbefd194904832a1153d68d36d6b269d81a8/ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b", size = 10103150 }, + { url = "https://files.pythonhosted.org/packages/44/f2/06cd9006077a8db61956768bc200a8e52515bf33a8f9b671ee527bb10d77/ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077", size = 10898637 }, + { url = "https://files.pythonhosted.org/packages/18/f5/af390a013c56022fe6f72b95c86eb7b2585c89cc25d63882d3bfe411ecf1/ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779", size = 10236012 }, + { url = "https://files.pythonhosted.org/packages/b8/ca/b9bf954cfed165e1a0c24b86305d5c8ea75def256707f2448439ac5e0d8b/ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794", size = 10415338 }, + { url = "https://files.pythonhosted.org/packages/d9/4d/2522dde4e790f1b59885283f8786ab0046958dfd39959c81acc75d347467/ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038", size = 9965277 }, + { url = "https://files.pythonhosted.org/packages/e5/7a/749f56f150eef71ce2f626a2f6988446c620af2f9ba2a7804295ca450397/ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f", size = 11541614 }, + { url = "https://files.pythonhosted.org/packages/89/b2/7d9b8435222485b6aac627d9c29793ba89be40b5de11584ca604b829e960/ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82", size = 12198873 }, + { url = "https://files.pythonhosted.org/packages/00/e0/a1a69ef5ffb5c5f9c31554b27e030a9c468fc6f57055886d27d316dfbabd/ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304", size = 11670190 }, + { url = "https://files.pythonhosted.org/packages/05/61/c1c16df6e92975072c07f8b20dad35cd858e8462b8865bc856fe5d6ccb63/ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470", size = 13902301 }, + { url = "https://files.pythonhosted.org/packages/79/89/0af10c8af4363304fd8cb833bd407a2850c760b71edf742c18d5a87bb3ad/ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a", size = 11350132 }, + { url = "https://files.pythonhosted.org/packages/b9/e1/ecb4c687cbf15164dd00e38cf62cbab238cad05dd8b6b0fc68b0c2785e15/ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b", size = 10312937 }, + { url = "https://files.pythonhosted.org/packages/cf/4f/0e53fe5e500b65934500949361e3cd290c5ba60f0324ed59d15f46479c06/ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a", size = 9936683 }, + { url = "https://files.pythonhosted.org/packages/04/a8/8183c4da6d35794ae7f76f96261ef5960853cd3f899c2671961f97a27d8e/ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159", size = 10950217 }, + { url = "https://files.pythonhosted.org/packages/26/88/9b85a5a8af21e46a0639b107fcf9bfc31da4f1d263f2fc7fbe7199b47f0a/ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783", size = 11404521 }, + { url = "https://files.pythonhosted.org/packages/fc/52/047f35d3b20fd1ae9ccfe28791ef0f3ca0ef0b3e6c1a58badd97d450131b/ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe", size = 10320697 }, + { url = "https://files.pythonhosted.org/packages/b9/fe/00c78010e3332a6e92762424cf4c1919065707e962232797d0b57fd8267e/ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800", size = 11378665 }, + { url = "https://files.pythonhosted.org/packages/43/7c/c83fe5cbb70ff017612ff36654edfebec4b1ef79b558b8e5fd933bab836b/ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e", size = 10460287 }, ] [[package]] @@ -4979,20 +4983,20 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.13.1" +version = "4.13.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 }, + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, ] [[package]] name = "urllib3" -version = "2.3.0" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, ] [[package]] From dced8d7f87bab006fe15aae5c43b30b7b36b2c08 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 14 Apr 2025 16:51:16 -0700 Subject: [PATCH 013/477] [bot] Update Python packages (#35017) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- uv.lock | 67 +++++++++++++++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 00e7a69382..d65fa99e2a 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 00e7a69382d37694ab585466c187c814f7de4c56 +Subproject commit d65fa99e2aa60a6d98cdfb5acb912665e9e6447e diff --git a/uv.lock b/uv.lock index 7141fa5195..6911f053cb 100644 --- a/uv.lock +++ b/uv.lock @@ -70,15 +70,15 @@ wheels = [ [[package]] name = "aioice" -version = "0.10.0" +version = "0.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dnspython" }, { name = "ifaddr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/8e/a68b686678c6c21e9d5a247d7b2849fd55e645179ebcd081889b53115854/aioice-0.10.0.tar.gz", hash = "sha256:25dfdcbacb89de0e2eac41e96a872499c37a9eb74fa22a0e92d8ac1a0a9141af", size = 44236 } +sdist = { url = "https://files.pythonhosted.org/packages/95/a2/45dfab1d5a7f96c48595a5770379acf406cdf02a2cd1ac1729b599322b08/aioice-0.10.1.tar.gz", hash = "sha256:5c8e1422103448d171925c678fb39795e5fe13d79108bebb00aa75a899c2094a", size = 44304 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/07/45f3f2864a8a5c1e0bf8618802dcc1d85f901cc13506a1d9eb93d1d2cbcb/aioice-0.10.0-py3-none-any.whl", hash = "sha256:e8c91083cc7858471554370d9037118d66582e25be24f4c6ad9ced2c2ba44bbe", size = 24882 }, + { url = "https://files.pythonhosted.org/packages/3b/58/af07dda649c22a1ae954ffb7aaaf4d4a57f1bf00ebdf62307affc0b8552f/aioice-0.10.1-py3-none-any.whl", hash = "sha256:f31ae2abc8608b1283ed5f21aebd7b6bd472b152ff9551e9b559b2d8efed79e9", size = 24872 }, ] [[package]] @@ -1482,32 +1482,39 @@ wheels = [ [[package]] name = "pillow" -version = "11.1.0" +version = "11.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 }, - { url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 }, - { url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 }, - { url = "https://files.pythonhosted.org/packages/e4/c2/e25199e7e4e71d64eeb869f5b72c7ddec70e0a87926398785ab944d92375/pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", size = 4402945 }, - { url = "https://files.pythonhosted.org/packages/c1/ed/51d6136c9d5911f78632b1b86c45241c712c5a80ed7fa7f9120a5dff1eba/pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", size = 4361228 }, - { url = "https://files.pythonhosted.org/packages/48/a4/fbfe9d5581d7b111b28f1d8c2762dee92e9821bb209af9fa83c940e507a0/pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", size = 4484021 }, - { url = "https://files.pythonhosted.org/packages/39/db/0b3c1a5018117f3c1d4df671fb8e47d08937f27519e8614bbe86153b65a5/pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", size = 4287449 }, - { url = "https://files.pythonhosted.org/packages/d9/58/bc128da7fea8c89fc85e09f773c4901e95b5936000e6f303222490c052f3/pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", size = 4419972 }, - { url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 }, - { url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 }, - { url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 }, - { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, - { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, - { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, - { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, - { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, - { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, - { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, - { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, - { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, - { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, - { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, + { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450 }, + { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550 }, + { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018 }, + { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006 }, + { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773 }, + { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069 }, + { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460 }, + { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304 }, + { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809 }, + { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338 }, + { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918 }, + { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185 }, + { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306 }, + { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121 }, + { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707 }, + { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921 }, + { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523 }, + { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836 }, + { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390 }, + { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309 }, + { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768 }, + { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087 }, + { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734 }, + { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841 }, + { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470 }, + { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013 }, + { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165 }, + { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586 }, + { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, ] [[package]] @@ -4751,15 +4758,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.25.1" +version = "2.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/2f/a0f732270cc7c1834f5ec45539aec87c360d5483a8bd788217a9102ccfbd/sentry_sdk-2.25.1.tar.gz", hash = "sha256:f9041b7054a7cf12d41eadabe6458ce7c6d6eea7a97cfe1b760b6692e9562cf0", size = 322190 } +sdist = { url = "https://files.pythonhosted.org/packages/f0/e2/f8ebb2086046d89cb692f12cc7f9d84e02669d19e263f9b0347553ac6e8c/sentry_sdk-2.26.0.tar.gz", hash = "sha256:88643459716dd0c6e412e5141fcc94ce3b5725e4b6b312210b91332b3b46a0e2", size = 322738 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/b6/84049ab0967affbc7cc7590d86ae0170c1b494edb69df8786707100420e5/sentry_sdk-2.25.1-py2.py3-none-any.whl", hash = "sha256:60b016d0772789454dc55a284a6a44212044d4a16d9f8448725effee97aaf7f6", size = 339851 }, + { url = "https://files.pythonhosted.org/packages/dd/da/195136b1113e5bc34f31ba04a345bc15c1e66a4eacfc1b317de111abe8c5/sentry_sdk-2.26.0-py2.py3-none-any.whl", hash = "sha256:82496fc359296dac57ec923300b18cc1f14a1279c1e7108d46d35dbb4cf8f5f8", size = 340184 }, ] [[package]] From abad7649d6395023ebf5443a836d3cc9c9dbfcfb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 14 Apr 2025 17:17:59 -0700 Subject: [PATCH 014/477] Rename experimental long (#35011) * now alpha long * fix * rename param * thought i did this * debug * debug * more * more * moar * Revert "moar" This reverts commit e347106246583982e1be206485ad4d8eb944f817. * Revert "more" This reverts commit 0746e6d2e400ce1715614c62af84a9680c4d65c1. * Revert "more" This reverts commit 0db5a8792c01bb2d687f9152e6ba37fb76d4a453. * Revert "debug" This reverts commit 72f235b13727980e4331ff487998ddc7c7f7fb63. * Revert "debug" This reverts commit 7871b69f8c1bdffed386c2baac99542fcc50f7eb. --- common/params_keys.h | 2 +- opendbc_repo | 2 +- selfdrive/car/card.py | 4 ++-- selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/tests/test_models.py | 10 +++++----- selfdrive/debug/set_car_params.py | 2 +- selfdrive/selfdrived/selfdrived.py | 4 ++-- selfdrive/test/process_replay/process_replay.py | 4 ++-- selfdrive/ui/qt/offroad/developer_panel.cc | 8 ++++---- selfdrive/ui/qt/offroad/settings.cc | 2 +- selfdrive/ui/qt/util.cc | 4 ++-- selfdrive/ui/tests/test_ui/run.py | 2 +- tools/sim/bridge/common.py | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index e630792357..ca779a5b5c 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -34,7 +34,7 @@ inline static std::unordered_map keys = { {"DoReboot", CLEAR_ON_MANAGER_START}, {"DoShutdown", CLEAR_ON_MANAGER_START}, {"DoUninstall", CLEAR_ON_MANAGER_START}, - {"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY}, + {"AlphaLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY}, {"ExperimentalMode", PERSISTENT}, {"ExperimentalModeConfirmed", PERSISTENT}, {"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, diff --git a/opendbc_repo b/opendbc_repo index d65fa99e2a..3b33a71a2b 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit d65fa99e2aa60a6d98cdfb5acb912665e9e6447e +Subproject commit 3b33a71a2bf08b57dd5b2bf77e26cc376dcd7cc9 diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 607f5590c4..378543164c 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -89,7 +89,7 @@ class Car: if len(can.can) > 0: break - experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") + alpha_long_allowed = self.params.get_bool("AlphaLongitudinalEnabled") num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) cached_params = None @@ -98,7 +98,7 @@ class Car: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params) + self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, num_pandas, cached_params) self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP) self.CP = self.CI.CP diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index e0cd5a1e91..92c7ff8f1c 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -39,7 +39,7 @@ class TestCarInterfaces: args = get_fuzzy_car_interface_args(data.draw) car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'], - experimental_long=args['experimental_long'], docs=False) + alpha_long=args['alpha_long'], docs=False) car_params = car_params.as_reader() car_interface = CarInterface(car_params) assert car_params diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 906fa99320..53f2bb586d 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -78,7 +78,7 @@ class TestCarModelBase(unittest.TestCase): cls.elm_frame = None cls.car_safety_mode_frame = None cls.fingerprint = gen_empty_fingerprint() - experimental_long = False + alpha_long = False for msg in lr: if msg.which() == "can": can = can_capnp_to_list((msg.as_builder().to_bytes(),))[0] @@ -91,7 +91,7 @@ class TestCarModelBase(unittest.TestCase): elif msg.which() == "carParams": car_fw = msg.carParams.carFw if msg.carParams.openpilotLongitudinalControl: - experimental_long = True + alpha_long = True if cls.platform is None: live_fingerprint = msg.carParams.carFingerprint cls.platform = MIGRATION.get(live_fingerprint, live_fingerprint) @@ -113,7 +113,7 @@ class TestCarModelBase(unittest.TestCase): cls.car_safety_mode_frame = len(can_msgs) assert len(can_msgs) > int(50 / DT_CTRL), "no can data found" - return car_fw, can_msgs, experimental_long + return car_fw, can_msgs, alpha_long @classmethod def get_testing_data(cls): @@ -146,13 +146,13 @@ class TestCarModelBase(unittest.TestCase): raise unittest.SkipTest raise Exception(f"missing test route for {cls.platform}") - car_fw, cls.can_msgs, experimental_long = cls.get_testing_data() + car_fw, cls.can_msgs, alpha_long = cls.get_testing_data() # if relay is expected to be open in the route cls.openpilot_enabled = cls.car_safety_mode_frame is not None cls.CarInterface = interfaces[cls.platform] - cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False) + cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, docs=False) assert cls.CP assert cls.CP.carFingerprint == cls.platform diff --git a/selfdrive/debug/set_car_params.py b/selfdrive/debug/set_car_params.py index 6060dfbc36..aec30b4d74 100755 --- a/selfdrive/debug/set_car_params.py +++ b/selfdrive/debug/set_car_params.py @@ -15,7 +15,7 @@ if __name__ == "__main__": else: CP = car.CarParams.new_message() CP.openpilotLongitudinalControl = True - CP.experimentalLongitudinalAvailable = False + CP.alphaLongitudinalAvailable = False cp_bytes = CP.to_bytes() for p in ("CarParams", "CarParamsCache", "CarParamsPersistent"): diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index c2e8259135..face7dab29 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -89,8 +89,8 @@ class SelfdriveD: car_recognized = self.CP.brand != 'mock' # cleanup old params - if not self.CP.experimentalLongitudinalAvailable: - self.params.remove("ExperimentalLongitudinalEnabled") + if not self.CP.alphaLongitudinalAvailable: + self.params.remove("AlphaLongitudinalEnabled") if not self.CP.openpilotLongitudinalControl: self.params.remove("ExperimentalMode") diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 9715e48615..6139846322 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -363,7 +363,7 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("ExperimentalLongitudinalEnabled"), cached_params=cached_params).CP + CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), cached_params=cached_params).CP if not params.get_bool("DisengageOnAccelerator"): CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS @@ -777,7 +777,7 @@ def generate_params_config(lr=None, CP=None, fingerprint=None, custom_params=Non params_dict["CarParamsCache"] = CP.as_builder().to_bytes() if CP.openpilotLongitudinalControl: - params_dict["ExperimentalLongitudinalEnabled"] = True + params_dict["AlphaLongitudinalEnabled"] = True if CP.notCar: params_dict["JoystickDebugMode"] = True diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc index 365aec09c4..a095228da2 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.cc +++ b/selfdrive/ui/qt/offroad/developer_panel.cc @@ -26,7 +26,7 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) { addItem(longManeuverToggle); experimentalLongitudinalToggle = new ParamControl( - "ExperimentalLongitudinalEnabled", + "AlphaLongitudinalEnabled", tr("openpilot Longitudinal Control (Alpha)"), QString("%1

%2") .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).")) @@ -68,8 +68,8 @@ void DeveloperPanel::updateToggles(bool _offroad) { capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); cereal::CarParams::Reader CP = cmsg.getRoot(); - if (!CP.getExperimentalLongitudinalAvailable() || is_release) { - params.remove("ExperimentalLongitudinalEnabled"); + if (!CP.getAlphaLongitudinalAvailable() || is_release) { + params.remove("AlphaLongitudinalEnabled"); experimentalLongitudinalToggle->setEnabled(false); } @@ -78,7 +78,7 @@ void DeveloperPanel::updateToggles(bool _offroad) { * - is not a release branch, and * - the car supports experimental longitudinal control (alpha) */ - experimentalLongitudinalToggle->setVisible(CP.getExperimentalLongitudinalAvailable() && !is_release); + experimentalLongitudinalToggle->setVisible(CP.getAlphaLongitudinalAvailable() && !is_release); longManeuverToggle->setEnabled(hasLongitudinalControl(CP) && _offroad); } else { diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 4d0516bda1..7a79413e2d 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -152,7 +152,7 @@ void TogglesPanel::updateToggles() { QString long_desc = unavailable + " " + \ tr("openpilot longitudinal control may come in a future update."); - if (CP.getExperimentalLongitudinalAvailable()) { + if (CP.getAlphaLongitudinalAvailable()) { if (is_release) { long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches."); } else { diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc index 399a1a98d7..ff381fe39c 100644 --- a/selfdrive/ui/qt/util.cc +++ b/selfdrive/ui/qt/util.cc @@ -193,8 +193,8 @@ QPixmap bootstrapPixmap(const QString &id) { bool hasLongitudinalControl(const cereal::CarParams::Reader &car_params) { // Using the experimental longitudinal toggle, returns whether longitudinal control // will be active without needing a restart of openpilot - return car_params.getExperimentalLongitudinalAvailable() - ? Params().getBool("ExperimentalLongitudinalEnabled") + return car_params.getAlphaLongitudinalAvailable() + ? Params().getBool("AlphaLongitudinalEnabled") : car_params.getOpenpilotLongitudinalControl(); } diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 35323cf000..c830680aa6 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -56,7 +56,7 @@ def setup_settings_firehose(click, pm: PubMaster): def setup_settings_developer(click, pm: PubMaster): CP = car.CarParams() - CP.experimentalLongitudinalAvailable = True + CP.alphaLongitudinalAvailable = True Params().put("CarParamsPersistent", CP.to_bytes()) setup_settings_device(click, pm) diff --git a/tools/sim/bridge/common.py b/tools/sim/bridge/common.py index bf3be5193d..69dec173ca 100644 --- a/tools/sim/bridge/common.py +++ b/tools/sim/bridge/common.py @@ -40,7 +40,7 @@ class SimulatorBridge(ABC): def __init__(self, dual_camera, high_quality): set_params_enabled() self.params = Params() - self.params.put_bool("ExperimentalLongitudinalEnabled", True) + self.params.put_bool("AlphaLongitudinalEnabled", True) self.rk = Ratekeeper(100, None) From 0a401620393798753afb6068b41e9faf630141d4 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 14 Apr 2025 18:36:55 -0700 Subject: [PATCH 015/477] Temporary disable UI preview from CI (#35018) disable --- .github/workflows/selfdrive_tests.yaml | 1 + .github/workflows/ui_preview.yaml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index b42dc7da26..25fc258634 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -331,6 +331,7 @@ jobs: runs-on: - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + if: false # FIXME: FrameReader is broken on CI runners steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/ui_preview.yaml b/.github/workflows/ui_preview.yaml index 4e8338c483..9ec7a59223 100644 --- a/.github/workflows/ui_preview.yaml +++ b/.github/workflows/ui_preview.yaml @@ -19,7 +19,8 @@ env: jobs: preview: - if: github.repository == 'commaai/openpilot' + #if: github.repository == 'commaai/openpilot' + if: false # FIXME: FrameReader is broken on CI runners name: preview runs-on: ubuntu-latest timeout-minutes: 20 From d7a5661ffe214f9f018e2cb889dae2292c1dd7d8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 14 Apr 2025 21:08:45 -0700 Subject: [PATCH 016/477] Fix possible CAN ignition overlap due to incorrect counter tracking (#35019) * fix-rivian-ign * bump --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 0dc95a89dd..d319ea2bba 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 0dc95a89dd7df1e27c36d7560c39666151af81df +Subproject commit d319ea2bbae4ee0f5dea12d7ca5456cd34288d55 From 60da5dd39a767e1f6515c1adc0ebbe146867acd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 17 Apr 2025 00:35:30 -0700 Subject: [PATCH 017/477] lagd: check sensors valid (#35027) * Check if sensors valid * Fix test * Fix static * Constants * time buffer for pose valid * Fix static --- selfdrive/locationd/lagd.py | 38 ++++++++++++++++++++------- selfdrive/locationd/test/test_lagd.py | 10 ++++--- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index 5ce82309ea..b52a658d01 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -21,8 +21,11 @@ MIN_OKAY_WINDOW_SEC = 25.0 MIN_RECOVERY_BUFFER_SEC = 2.0 MIN_VEGO = 15.0 MIN_ABS_YAW_RATE = np.radians(1.0) +MAX_YAW_RATE_SANITY_CHECK = 1.0 MIN_NCC = 0.95 MAX_LAG = 1.0 +MAX_LAT_ACCEL = 2.0 +MAX_LAT_ACCEL_DIFF = 0.6 def masked_normalized_cross_correlation(expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, n: int): @@ -139,7 +142,8 @@ class LateralLagEstimator: def __init__(self, CP: car.CarParams, dt: float, block_count: int = BLOCK_NUM, min_valid_block_count: int = BLOCK_NUM_NEEDED, block_size: int = BLOCK_SIZE, window_sec: float = MOVING_WINDOW_SEC, okay_window_sec: float = MIN_OKAY_WINDOW_SEC, min_recovery_buffer_sec: float = MIN_RECOVERY_BUFFER_SEC, - min_vego: float = MIN_VEGO, min_yr: float = MIN_ABS_YAW_RATE, min_ncc: float = MIN_NCC): + min_vego: float = MIN_VEGO, min_yr: float = MIN_ABS_YAW_RATE, min_ncc: float = MIN_NCC, + max_lat_accel: float = MAX_LAT_ACCEL, max_lat_accel_diff: float = MAX_LAT_ACCEL_DIFF): self.dt = dt self.window_sec = window_sec self.okay_window_sec = okay_window_sec @@ -151,6 +155,8 @@ class LateralLagEstimator: self.min_vego = min_vego self.min_yr = min_yr self.min_ncc = min_ncc + self.max_lat_accel = max_lat_accel + self.max_lat_accel_diff = max_lat_accel_diff self.t = 0.0 self.lat_active = False @@ -159,10 +165,13 @@ class LateralLagEstimator: self.desired_curvature = 0.0 self.v_ego = 0.0 self.yaw_rate = 0.0 + self.yaw_rate_std = 0.0 + self.pose_valid = False self.last_lat_inactive_t = 0.0 self.last_steering_pressed_t = 0.0 self.last_steering_saturated_t = 0.0 + self.last_pose_invalid_t = 0.0 self.last_estimate_t = 0.0 self.calibrator = PoseCalibrator() @@ -212,7 +221,9 @@ class LateralLagEstimator: elif which == "livePose": device_pose = Pose.from_live_pose(msg) calibrated_pose = self.calibrator.build_calibrated_pose(device_pose) - self.yaw_rate = calibrated_pose.angular_velocity.z + self.yaw_rate = calibrated_pose.angular_velocity.yaw + self.yaw_rate_std = calibrated_pose.angular_velocity.yaw_std + self.pose_valid = msg.angularVelocityDevice.valid and msg.posenetOK and msg.inputsOK self.t = t def points_enough(self): @@ -222,23 +233,30 @@ class LateralLagEstimator: return self.points.num_okay >= int(self.okay_window_sec / self.dt) def update_points(self): + la_desired = self.desired_curvature * self.v_ego * self.v_ego + la_actual_pose = self.yaw_rate * self.v_ego + + fast = self.v_ego > self.min_vego + turning = np.abs(self.yaw_rate) >= self.min_yr + sensors_valid = self.pose_valid and np.abs(self.yaw_rate) < MAX_YAW_RATE_SANITY_CHECK and self.yaw_rate_std < MAX_YAW_RATE_SANITY_CHECK + la_valid = np.abs(la_actual_pose) <= self.max_lat_accel and np.abs(la_desired - la_actual_pose) <= self.max_lat_accel_diff + calib_valid = self.calibrator.calib_valid + if not self.lat_active: self.last_lat_inactive_t = self.t if self.steering_pressed: self.last_steering_pressed_t = self.t if self.steering_saturated: self.last_steering_saturated_t = self.t + if not sensors_valid or not la_valid: + self.last_pose_invalid_t = self.t - la_desired = self.desired_curvature * self.v_ego * self.v_ego - la_actual_pose = self.yaw_rate * self.v_ego - - fast = self.v_ego > self.min_vego - turning = np.abs(self.yaw_rate) >= self.min_yr - has_recovered = all( # wait for recovery after !lat_active, steering_pressed, steering_saturated + has_recovered = all( # wait for recovery after !lat_active, steering_pressed, steering_saturated, !sensors/la_valid self.t - last_t >= self.min_recovery_buffer_sec - for last_t in [self.last_lat_inactive_t, self.last_steering_pressed_t, self.last_steering_saturated_t] + for last_t in [self.last_lat_inactive_t, self.last_steering_pressed_t, self.last_steering_saturated_t, self.last_pose_invalid_t] ) - okay = self.lat_active and not self.steering_pressed and not self.steering_saturated and fast and turning and has_recovered + okay = self.lat_active and not self.steering_pressed and not self.steering_saturated and \ + fast and turning and has_recovered and calib_valid and sensors_valid and la_valid self.points.update(self.t, la_desired, la_actual_pose, okay) diff --git a/selfdrive/locationd/test/test_lagd.py b/selfdrive/locationd/test/test_lagd.py index 53af2d2573..bd5cfd6201 100644 --- a/selfdrive/locationd/test/test_lagd.py +++ b/selfdrive/locationd/test/test_lagd.py @@ -3,7 +3,7 @@ import numpy as np import time import pytest -from cereal import messaging +from cereal import messaging, log from openpilot.selfdrive.locationd.lagd import LateralLagEstimator, retrieve_initial_lag, masked_normalized_cross_correlation, \ BLOCK_NUM_NEEDED, BLOCK_SIZE, MIN_OKAY_WINDOW_SEC from openpilot.selfdrive.test.process_replay.migration import migrate, migrate_carParams @@ -23,8 +23,8 @@ def process_messages(mocker, estimator, lag_frames, n_frames, vego=20.0, rejecti for i in range(n_frames): t = i * estimator.dt - desired_la = np.cos(t) - actual_la = np.cos(t - lag_frames * estimator.dt) + desired_la = np.cos(t) * 0.1 + actual_la = np.cos(t - lag_frames * estimator.dt) * 0.1 # if sample is masked out, set it to desired value (no lag) rejected = random.uniform(0, 1) < rejection_threshold @@ -41,7 +41,9 @@ def process_messages(mocker, estimator, lag_frames, n_frames, vego=20.0, rejecti (t, "livePose", mocker.Mock(orientationNED=ZeroMock(), velocityDevice=ZeroMock(), accelerationDevice=ZeroMock(), - angularVelocityDevice=ZeroMock(z=actual_yr))), + angularVelocityDevice=ZeroMock(z=actual_yr, valid=True), + posenetOK=True, inputsOK=True)), + (t, "liveCalibration", mocker.Mock(rpyCalib=[0, 0, 0], calStatus=log.LiveCalibrationData.Status.calibrated)), ] for t, w, m in msgs: estimator.handle_log(t, w, m) From c933914f8679836f2b7a5b733a90086b266d5bc7 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 17 Apr 2025 23:11:19 +0100 Subject: [PATCH 018/477] ui(raylib): update spinner progress bar to match Qt (#35028) --- system/ui/spinner.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 9809307474..951c6f697e 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -13,6 +13,7 @@ ROTATION_TIME_SECONDS = 1.0 # Time for one full circle MARGIN = 200 TEXTURE_SIZE = 360 FONT_SIZE = 80 +DARKGRAY = (55, 55, 55, 255) def clamp(value, min_value, max_value): @@ -57,10 +58,10 @@ class Spinner: if text.isdigit(): progress = clamp(int(text), 0, 100) bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT) - rl.draw_rectangle_rounded(bar, 0.5, 10, rl.GRAY) + rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY) bar.width *= progress / 100.0 - rl.draw_rectangle_rounded(bar, 0.5, 10, rl.WHITE) + rl.draw_rectangle_rounded(bar, 1, 10, rl.WHITE) else: text_size = rl.measure_text_ex(gui_app.font(), text, FONT_SIZE, 1.0) rl.draw_text_ex(gui_app.font(), text, From 06a9483a24d9d5e9fa2b32222d77c89e9ea16148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 17 Apr 2025 16:49:30 -0700 Subject: [PATCH 019/477] lagd: estimate std (#35009) * Std * Fix static * Refactor * Assert std zero --- cereal/log.capnp | 1 + selfdrive/locationd/lagd.py | 39 ++++++++++++++++++++------- selfdrive/locationd/test/test_lagd.py | 2 ++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/cereal/log.capnp b/cereal/log.capnp index 797c2cd8ac..c624eb0052 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -2284,6 +2284,7 @@ struct LiveDelayData { status @2 :Status; lateralDelayEstimate @3 :Float32; + lateralDelayEstimateStd @5 :Float32; points @4 :List(Float32); enum Status { diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index b52a658d01..0a7c71a7ea 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -24,6 +24,7 @@ MIN_ABS_YAW_RATE = np.radians(1.0) MAX_YAW_RATE_SANITY_CHECK = 1.0 MIN_NCC = 0.95 MAX_LAG = 1.0 +MAX_LAG_STD = 0.1 MAX_LAT_ACCEL = 2.0 MAX_LAT_ACCEL_DIFF = 0.6 @@ -127,13 +128,23 @@ class BlockAverage: self.block_idx = (self.block_idx + 1) % self.num_blocks self.valid_blocks = min(self.valid_blocks + 1, self.num_blocks) - def get(self) -> tuple[float, float]: + def get(self) -> tuple[float, float, float, float]: valid_block_idx = [i for i in range(self.valid_blocks) if i != self.block_idx] valid_and_current_idx = valid_block_idx + ([self.block_idx] if self.idx > 0 else []) - valid_mean = float(np.mean(self.values[valid_block_idx], axis=0).item()) if len(valid_block_idx) > 0 else float('nan') - current_mean = float(np.mean(self.values[valid_and_current_idx], axis=0).item()) if len(valid_and_current_idx) > 0 else float('nan') - return valid_mean, current_mean + if len(valid_block_idx) > 0: + valid_mean = float(np.mean(self.values[valid_block_idx], axis=0).item()) + valid_std = float(np.std(self.values[valid_block_idx], axis=0).item()) + else: + valid_mean, valid_std = float('nan'), float('nan') + + if len(valid_and_current_idx) > 0: + current_mean = float(np.mean(self.values[valid_and_current_idx], axis=0).item()) + current_std = float(np.std(self.values[valid_and_current_idx], axis=0).item()) + else: + current_mean, current_std = float('nan'), float('nan') + + return valid_mean, valid_std, current_mean, current_std class LateralLagEstimator: @@ -190,17 +201,27 @@ class LateralLagEstimator: liveDelay = msg.liveDelay - valid_mean_lag, current_mean_lag = self.block_avg.get() - if self.block_avg.valid_blocks >= self.min_valid_block_count and not np.isnan(valid_mean_lag): - liveDelay.status = log.LiveDelayData.Status.estimated - liveDelay.lateralDelay = valid_mean_lag + valid_mean_lag, valid_std, current_mean_lag, current_std = self.block_avg.get() + if self.block_avg.valid_blocks >= self.min_valid_block_count and not np.isnan(valid_mean_lag) and not np.isnan(valid_std): + if valid_std > MAX_LAG_STD: + liveDelay.status = log.LiveDelayData.Status.invalid + else: + liveDelay.status = log.LiveDelayData.Status.estimated else: liveDelay.status = log.LiveDelayData.Status.unestimated + + if liveDelay.status == log.LiveDelayData.Status.estimated: + liveDelay.lateralDelay = valid_mean_lag + else: liveDelay.lateralDelay = self.initial_lag - if not np.isnan(current_mean_lag): + + if not np.isnan(current_mean_lag) and not np.isnan(current_std): liveDelay.lateralDelayEstimate = current_mean_lag + liveDelay.lateralDelayEstimateStd = current_std else: liveDelay.lateralDelayEstimate = self.initial_lag + liveDelay.lateralDelayEstimateStd = 0.0 + liveDelay.validBlocks = self.block_avg.valid_blocks if debug: liveDelay.points = self.block_avg.values.flatten().tolist() diff --git a/selfdrive/locationd/test/test_lagd.py b/selfdrive/locationd/test/test_lagd.py index bd5cfd6201..13aea60a26 100644 --- a/selfdrive/locationd/test/test_lagd.py +++ b/selfdrive/locationd/test/test_lagd.py @@ -113,6 +113,7 @@ class TestLagd: assert msg.liveDelay.status == 'estimated' assert np.allclose(msg.liveDelay.lateralDelay, lag_frames * DT, atol=0.01) assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01) + assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01) assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED def test_estimator_masking(self, mocker): @@ -121,6 +122,7 @@ class TestLagd: process_messages(mocker, estimator, lag_frames, (int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_SIZE) * 2, rejection_threshold=0.4) msg = estimator.get_msg(True) assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01) + assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01) @pytest.mark.skipif(PC, reason="only on device") @pytest.mark.timeout(60) From a4cdc96a99be6a2da574d3327f7e76fbd6f63dd3 Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Thu, 17 Apr 2025 16:58:00 -0700 Subject: [PATCH 020/477] update --- cereal/log.capnp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cereal/log.capnp b/cereal/log.capnp index c624eb0052..a5828d61cb 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -1174,6 +1174,8 @@ struct ModelDataV2 { struct Action { desiredCurvature @0 :Float32; + desiredAcceleration @1 :Float32; + shouldStop @2 :Bool; } } From 4f913f0cfba188f95bc565e225c41f842168a982 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 18 Apr 2025 01:11:22 +0100 Subject: [PATCH 021/477] ui(raylib): fix typos (#35030) --- system/ui/lib/application.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index fd1ad52068..470396bbc7 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -73,7 +73,7 @@ class GuiApplication: for font in self._fonts.values(): rl.unload_font(font) - self._fonts = [] + self._fonts = {} rl.close_window() @@ -90,8 +90,8 @@ class GuiApplication: rl.end_drawing() self._monitor_fps() - def font(self, font_wight: FontWeight=FontWeight.NORMAL): - return self._fonts[font_wight] + def font(self, font_weight: FontWeight=FontWeight.NORMAL): + return self._fonts[font_weight] @property def width(self): From b42ec33a6375e831f11ca88f5d92c1f0d14efa64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Thu, 17 Apr 2025 17:59:15 -0700 Subject: [PATCH 022/477] Update modeld action logic (#35032) * add action logic * magic numbers * unused * plot accel * more changes --- selfdrive/controls/lib/drive_helpers.py | 18 +++++++- .../controls/lib/longitudinal_planner.py | 21 +--------- selfdrive/modeld/fill_model_msg.py | 9 ++-- selfdrive/modeld/modeld.py | 42 ++++++++++++++++--- selfdrive/test/process_replay/model_replay.py | 1 + 5 files changed, 62 insertions(+), 29 deletions(-) diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 41384abae8..9958755577 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -1,7 +1,7 @@ import numpy as np from cereal import log from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY -from openpilot.common.realtime import DT_CTRL +from openpilot.common.realtime import DT_CTRL, DT_MDL MIN_SPEED = 1.0 CONTROL_N = 17 @@ -43,3 +43,19 @@ def get_speed_error(modelV2: log.ModelDataV2, v_ego: float) -> float: vel_err = np.clip(modelV2.temporalPose.trans[0] - v_ego, -MAX_VEL_ERR, MAX_VEL_ERR) return float(vel_err) return 0.0 + + +def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05): + if len(speeds) == len(t_idxs): + v_now = speeds[0] + a_now = accels[0] + v_target = np.interp(action_t, t_idxs, speeds) + a_target = 2 * (v_target - v_now) / (action_t) - a_now + v_target_1sec = np.interp(action_t + 1.0, t_idxs, speeds) + else: + v_target = 0.0 + v_target_1sec = 0.0 + a_target = 0.0 + should_stop = (v_target < vEgoStopping and + v_target_1sec < vEgoStopping) + return a_target, should_stop diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index c7a2295a5f..8cbf0beee9 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -11,7 +11,7 @@ from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC -from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_speed_error +from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_speed_error, get_accel_from_plan from openpilot.selfdrive.car.cruise import V_CRUISE_MAX, V_CRUISE_UNSET from openpilot.common.swaglog import cloudlog @@ -48,23 +48,6 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP): return [a_target[0], min(a_target[1], a_x_allowed)] -def get_accel_from_plan(speeds, accels, action_t=DT_MDL, vEgoStopping=0.05): - if len(speeds) == CONTROL_N: - v_now = speeds[0] - a_now = accels[0] - - v_target = np.interp(action_t, CONTROL_N_T_IDX, speeds) - a_target = 2 * (v_target - v_now) / (action_t) - a_now - v_target_1sec = np.interp(action_t + 1.0, CONTROL_N_T_IDX, speeds) - else: - v_target = 0.0 - v_target_1sec = 0.0 - a_target = 0.0 - should_stop = (v_target < vEgoStopping and - v_target_1sec < vEgoStopping) - return a_target, should_stop - - class LongitudinalPlanner: def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL): self.CP = CP @@ -176,7 +159,7 @@ class LongitudinalPlanner: self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 action_t = self.CP.longitudinalActuatorDelay + DT_MDL - output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, + output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, action_t=action_t, vEgoStopping=self.CP.vEgoStopping) for idx in range(2): diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 9dc2641c9b..a91c6395c7 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -56,7 +56,7 @@ def fill_lane_line_meta(builder, lane_lines, lane_line_probs): builder.rightProb = lane_line_probs[2] def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._DynamicStructBuilder, - net_output_data: dict[str, np.ndarray], v_ego: float, delay: float, + net_output_data: dict[str, np.ndarray], action: log.ModelDataV2.Action, publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float, valid: bool) -> None: @@ -71,7 +71,8 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D driving_model_data.frameIdExtra = vipc_frame_id_extra driving_model_data.frameDropPerc = frame_drop_perc driving_model_data.modelExecutionTime = model_execution_time - driving_model_data.action.desiredCurvature = float(net_output_data['desired_curvature'][0,0]) + + driving_model_data.action = action modelV2 = extended_msg.modelV2 modelV2.frameId = vipc_frame_id @@ -98,8 +99,8 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D # poly path fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) - # lateral planning - modelV2.action.desiredCurvature = float(net_output_data['desired_curvature'][0,0]) + # action + modelV2.action = action # times at X_IDXS of edges and lines aren't used LINE_T_IDXS: list[float] = [] diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 1e557942fe..f5007273ec 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -21,17 +21,20 @@ from opendbc.car.car_helpers import get_demo_car_params from openpilot.common.swaglog import cloudlog from openpilot.common.params import Params from openpilot.common.filter_simple import FirstOrderFilter -from openpilot.common.realtime import config_realtime_process +from openpilot.common.realtime import config_realtime_process, DT_MDL from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState -from openpilot.selfdrive.modeld.constants import ModelConstants +from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext + + PROCESS_NAME = "selfdrive.modeld.modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -40,6 +43,30 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' +LAT_SMOOTH_SECONDS = 0.0 +LONG_SMOOTH_SECONDS = 0.0 + +def smooth_value(val, prev_val, tau): + alpha = 1 - np.exp(-DT_MDL / tau) if tau > 0 else 1 + return alpha * val + (1 - alpha) * prev_val + + +def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, + lat_action_t: float, long_action_t: float,) -> log.ModelDataV2.Action: + plan = model_output['plan'][0] + desired_accel, should_stop = get_accel_from_plan(plan[:,Plan.VELOCITY][:,0], + plan[:,Plan.ACCELERATION][:,0], + ModelConstants.T_IDXS, + action_t=long_action_t) + desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS) + + desired_curvature = model_output['desired_curvature'][0, 0] + desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) + + return log.ModelDataV2.Action(desiredCurvature=float(desired_curvature), + desiredAcceleration=float(desired_accel), + shouldStop=bool(should_stop)) + class FrameMeta: frame_id: int = 0 timestamp_sof: int = 0 @@ -221,7 +248,10 @@ def main(demo=False): cloudlog.info("modeld got CarParams: %s", CP.brand) # TODO this needs more thought, use .2s extra for now to estimate other delays - steer_delay = CP.steerActuatorDelay + .2 + # TODO Move smooth seconds to action function + lat_delay = CP.steerActuatorDelay + .2 + LAT_SMOOTH_SECONDS + long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS + prev_action = log.ModelDataV2.Action() DH = DesireHelper() @@ -263,7 +293,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - lateral_control_params = np.array([v_ego, steer_delay], dtype=np.float32) + lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -306,7 +336,9 @@ def main(demo=False): modelv2_send = messaging.new_message('modelV2') drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - fill_model_msg(drivingdata_send, modelv2_send, model_output, v_ego, steer_delay, + + action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL) + fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen) diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py index 36398af8ca..af626e2da9 100755 --- a/selfdrive/test/process_replay/model_replay.py +++ b/selfdrive/test/process_replay/model_replay.py @@ -64,6 +64,7 @@ def generate_report(proposed, master, tmp, commit): ModelV2_Plots = zl([ (lambda x: get_idx_if_non_empty(x.velocity.x, 0), "velocity.x"), (lambda x: get_idx_if_non_empty(x.action.desiredCurvature), "desiredCurvature"), + (lambda x: get_idx_if_non_empty(x.action.desiredAcceleration), "desiredAcceleration"), (lambda x: get_idx_if_non_empty(x.leadsV3[0].x, 0), "leadsV3.x"), (lambda x: get_idx_if_non_empty(x.laneLines[1].y, 0), "laneLines.y"), (lambda x: get_idx_if_non_empty(x.meta.desireState, 3), "desireState.laneChangeLeft"), From 15326c2d301427ce525bce464d8d1ca84fd3c4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 17 Apr 2025 18:37:48 -0700 Subject: [PATCH 023/477] lagd: check for validity of the estimate when restoring state (#35034) Do not restore if invalid --- selfdrive/locationd/lagd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index 0a7c71a7ea..f4f46b7469 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -327,8 +327,9 @@ def retrieve_initial_lag(params_reader: Params, CP: car.CarParams): if last_CP.carFingerprint != CP.carFingerprint: raise Exception("Car model mismatch") - lag, valid_blocks = ld.lateralDelayEstimate, ld.validBlocks + lag, valid_blocks, status = ld.lateralDelayEstimate, ld.validBlocks, ld.status assert valid_blocks <= BLOCK_NUM, "Invalid number of valid blocks" + assert status != log.LiveDelayData.Status.invalid, "Lag estimate is invalid" return lag, valid_blocks except Exception as e: cloudlog.error(f"Failed to retrieve initial lag: {e}") From 65c210265b23ac6ca16a0f5532c880465aa41ab1 Mon Sep 17 00:00:00 2001 From: niko001 Date: Fri, 18 Apr 2025 05:55:32 +0200 Subject: [PATCH 024/477] Multilang: Update de translation (#35025) added missing German translations --- selfdrive/ui/translations/main_de.ts | 598 ++++++++++++++------------- 1 file changed, 301 insertions(+), 297 deletions(-) diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 560e1df2cc..f279a8d9f1 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -3,14 +3,14 @@ AbstractAlert - + Close Schließen - - + + Snooze Update Update pausieren - + Reboot and Update Aktualisieren und neu starten @@ -66,26 +66,26 @@ Prevent large data uploads when on a metered connection Hochladen großer Dateien über getaktete Verbindungen unterbinden - - Hidden Network - - - - CONNECT - CONNECT - - - Enter SSID - SSID eingeben - - - Enter password - Passwort eingeben - - - for "%1" - für "%1" - + + Hidden Network + Verborgenes Netzwerk + + + CONNECT + VERBINDEN + + + Enter SSID + SSID eingeben + + + Enter password + Passwort eingeben + + + for "%1" + für "%1" + ConfirmationDialog @@ -115,34 +115,34 @@ DeveloperPanel - - Joystick Debug Mode - - - - Longitudinal Maneuver Mode - - - - openpilot Longitudinal Control (Alpha) - - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - - - - On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. - - - - Enable ADB - - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - - + + Joystick Debug Mode + Joystick Debug-Modus + + + Longitudinal Maneuver Mode + Längsmanöver-Modus + + + openpilot Longitudinal Control (Alpha) + openpilot Längsregelung (Alpha) + + + WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + WARNUNG: Die openpilot Längsregelung befindet sich für dieses Fahrzeug im Alpha-Stadium und deaktiviert das automatische Notbremsen (AEB). + + + On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. + Bei diesem Fahrzeug verwendet openpilot standardmäßig den eingebauten Tempomaten anstelle der openpilot Längsregelung. Aktiviere diese Option, um auf die openpilot Längsregelung umzuschalten. Es wird empfohlen, den experimentellen Modus zu aktivieren, wenn die openpilot Längsregelung (Alpha) aktiviert wird. + + + Enable ADB + ADB aktivieren + + + ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. + ADB (Android Debug Bridge) ermöglicht die Verbindung zu deinem Gerät über USB oder Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-comma für weitere Informationen. + DevicePanel @@ -278,14 +278,14 @@ Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. Koppele dein Gerät mit Comma Connect (connect.comma.ai) und sichere dir dein Comma Prime Angebot. - - Pair Device - - - - PAIR - - + + Pair Device + Gerät koppeln + + + PAIR + KOPPELN + DriverViewWindow @@ -307,39 +307,41 @@ FirehosePanel - - 🔥 Firehose Mode 🔥 - - - - openpilot learns to drive by watching humans, like you, drive. + + 🔥 Firehose Mode 🔥 + 🔥 Firehose-Modus 🔥 + + + openpilot learns to drive by watching humans, like you, drive. Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode. - - - - Firehose Mode: ACTIVE - - - - ACTIVE - - - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - - - - <b>%n segment(s)</b> of your driving is in the training dataset so far. - - - - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - - + openpilot lernt das Fahren, indem es Menschen wie dir beim Fahren zuschaut. + +Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximieren, um die Fahrmodelle von openpilot zu verbessern. Mehr Daten bedeuten größere Modelle, was zu einem besseren Experimentellen Modus führt. + + + Firehose Mode: ACTIVE + Firehose-Modus: AKTIV + + + ACTIVE + AKTIV + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. + Für maximale Effektivität bring dein Gerät jede Woche nach drinnen und verbinde es mit einem guten USB-C-Adapter und WLAN.<br><br>Der Firehose-Modus funktioniert auch während der Fahrt, wenn das Gerät mit einem Hotspot oder einer ungedrosselten SIM-Karte verbunden ist.<br><br><br><b>Häufig gestellte Fragen</b><br><br><i>Spielt es eine Rolle, wie oder wo ich fahre?</i> Nein, fahre einfach wie gewohnt.<br><br><i>Werden im Firehose-Modus alle meine Segmente hochgeladen?</i> Nein, wir wählen selektiv nur einen Teil deiner Segmente aus.<br><br><i>Welcher USB-C-Adapter ist gut?</i> Jedes Schnellladegerät für Handy oder Laptop sollte ausreichen.<br><br><i>Spielt es eine Rolle, welche Software ich nutze?</i> Ja, nur das offizielle Upstream‑openpilot (und bestimmte Forks) kann für das Training verwendet werden. + + + <b>%n segment(s)</b> of your driving is in the training dataset so far. + + <b>%n Segment</b> deiner Fahrten ist bisher im Trainingsdatensatz. + <b>%n Segmente</b> deiner Fahrten sind bisher im Trainingsdatensatz. + + + + <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network + <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INAKTIV</span>: Verbinde dich mit einem ungedrosselten Netzwerk + HudRenderer @@ -411,49 +413,50 @@ Firehose Mode allows you to maximize your training data uploads to improve openp OffroadAlert Immediately connect to the internet to check for updates. If you do not connect to the internet, openpilot won't engage in %1 - + Stelle sofort eine Internetverbindung her, um nach Updates zu suchen. Wenn du keine Verbindung herstellst, kann openpilot in %1 nicht mehr aktiviert werden. Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. - + Verbinde dich mit dem Internet, um nach Updates zu suchen. openpilot startet nicht automatisch, bis eine Internetverbindung besteht und nach Updates gesucht wurde. - - Unable to download updates + + Unable to download updates %1 - - - - Taking camera snapshots. System won't start until finished. - - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - - - - Device failed to register. It will not connect to or upload to comma.ai servers, and receives no support from comma.ai. If this is an official device, visit https://comma.ai/support. - - - - NVMe drive not mounted. - - - - Unsupported NVMe drive detected. Device may draw significantly more power and overheat due to the unsupported NVMe. - - - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - - + Updates konnten nicht heruntergeladen werden +%1 + + + Taking camera snapshots. System won't start until finished. + Kamera-Snapshots werden aufgenommen. Das System startet erst, wenn dies abgeschlossen ist. + + + An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. + Ein Update für das Betriebssystem deines Geräts wird im Hintergrund heruntergeladen. Du wirst aufgefordert, das Update zu installieren, sobald es bereit ist. + + + Device failed to register. It will not connect to or upload to comma.ai servers, and receives no support from comma.ai. If this is an official device, visit https://comma.ai/support. + Gerät konnte nicht registriert werden. Es wird keine Verbindung zu den comma.ai-Servern herstellen oder Daten hochladen und erhält keinen Support von comma.ai. Wenn dies ein offizielles Gerät ist, besuche https://comma.ai/support. + + + NVMe drive not mounted. + NVMe-Laufwerk nicht gemounted. + + + Unsupported NVMe drive detected. Device may draw significantly more power and overheat due to the unsupported NVMe. + Nicht unterstütztes NVMe-Laufwerk erkannt. Das Gerät kann dadurch deutlich mehr Strom verbrauchen und überhitzen. + + + openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + openpilot konnte dein Auto nicht identifizieren. Dein Auto wird entweder nicht unterstützt oder die Steuergeräte (ECUs) werden nicht erkannt. Bitte reiche einen Pull Request ein, um die Firmware-Versionen für das richtige Fahrzeug hinzuzufügen. Hilfe findest du auf discord.comma.ai. + + + openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + openpilot hat eine Änderung der Montageposition des Geräts erkannt. Stelle sicher, dass das Gerät vollständig in der Halterung sitzt und die Halterung fest an der Windschutzscheibe befestigt ist. + + + Device temperature too high. System cooling down before starting. Current internal component temperature: %1 + Gerätetemperatur zu hoch. Das System kühlt ab, bevor es startet. Aktuelle interne Komponententemperatur: %1 + OffroadHome @@ -472,26 +475,26 @@ Firehose Mode allows you to maximize your training data uploads to improve openp OnroadAlerts - - openpilot Unavailable - - - - TAKE CONTROL IMMEDIATELY - - - - Reboot Device - - - - Waiting to start - - - - System Unresponsive - - + + openpilot Unavailable + openpilot nicht verfügbar + + + TAKE CONTROL IMMEDIATELY + ÜBERNIMM SOFORT DIE KONTROLLE + + + Reboot Device + Gerät neu starten + + + Waiting to start + Warten auf Start + + + System Unresponsive + System reagiert nicht + PairingPopup @@ -511,10 +514,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Bookmark connect.comma.ai to your home screen to use it like an app Füge connect.comma.ai als Lesezeichen auf deinem Homescreen hinzu um es wie eine App zu verwenden - - Please connect to Wi-Fi to complete initial pairing - - + + Please connect to Wi-Fi to complete initial pairing + Bitte verbinde dich mit WLAN, um die Koppelung abzuschließen. + ParamControl @@ -545,18 +548,18 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Remote access Fernzugriff - - 24/7 LTE connectivity - - - - 1 year of drive storage - - - - Remote snapshots - - + + 24/7 LTE connectivity + 24/7 LTE-Verbindung + + + 1 year of drive storage + Fahrdaten-Speicherung für 1 Jahr + + + Remote snapshots + Remote-Snapshots + PrimeUserWidget @@ -604,10 +607,10 @@ Firehose Mode allows you to maximize your training data uploads to improve openp vor %n Tagen - - now - - + + now + jetzt + Reset @@ -635,19 +638,20 @@ Firehose Mode allows you to maximize your training data uploads to improve openp Confirm Bestätigen - - Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. - - - - Resetting device... + + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + Datenpartition konnte nicht gemounted werden. Die Partition ist möglicherweise beschädigt. Drücke Bestätigen, um das Gerät zu löschen und zurückzusetzen. + + + Resetting device... This may take up to a minute. - - - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - - + Gerät wird zurückgesetzt... +Dies kann bis zu einer Minute dauern. + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + System-Reset ausgelöst. Drücke Bestätigen, um alle Inhalte und Einstellungen zu löschen. Drücke Abbrechen, um den Startvorgang fortzusetzen. + SettingsWindow @@ -671,14 +675,14 @@ This may take up to a minute. Software Software - - Developer - - - - Firehose - - + + Developer + Entwickler + + + Firehose + Firehose + Setup @@ -750,30 +754,30 @@ This may take up to a minute. Start over Von neuem beginnen - - No custom software found at this URL. - - - - Something went wrong. Reboot the device. - - + + No custom software found at this URL. + Keine benutzerdefinierte Software unter dieser URL gefunden. + + + Something went wrong. Reboot the device. + Etwas ist schiefgelaufen. Starte das Gerät neu. + Select a language Sprache wählen - - Choose Software to Install - - - - openpilot - openpilot - - - Custom Software - - + + Choose Software to Install + Wähle die zu installierende Software + + + openpilot + openpilot + + + Custom Software + Benutzerdefinierte Software + SetupWidget @@ -921,26 +925,26 @@ This may take up to a minute. Uninstall Deinstallieren - - failed to check for update - - - - up to date, last checked %1 - - - - DOWNLOAD - - - - update available - - - - never - - + + failed to check for update + Update-Prüfung fehlgeschlagen + + + up to date, last checked %1 + Auf dem neuesten Stand, zuletzt geprüft am %1 + + + DOWNLOAD + HERUNTERLADEN + + + update available + Update verfügbar + + + never + nie + SshControl @@ -998,14 +1002,14 @@ This may take up to a minute. Agree Zustimmen - - Welcome to openpilot - - - - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - - + + Welcome to openpilot + Willkommen bei openpilot + + + You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + Du musst die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden. Lies die aktuellen Bedingungen unter <span style='color: #465BEA;'>https://comma.ai/terms</span>, bevor du fortfährst. + TogglesPanel @@ -1069,54 +1073,54 @@ This may take up to a minute. Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. Der experimentelle Modus ist momentan für dieses Auto nicht verfügbar da es den eingebauten adaptiven Tempomaten des Autos benutzt. - - Aggressive - - - - Standard - - - - Relaxed - - - - Driving Personality - - - - End-to-End Longitudinal Control - - - - openpilot longitudinal control may come in a future update. - - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - - - - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. - - - - Always-On Driver Monitoring - - - - Enable driver monitoring even when openpilot is not engaged. - - + + Aggressive + Aggressiv + + + Standard + Standard + + + Relaxed + Entspannt + + + Driving Personality + Fahrstil + + + End-to-End Longitudinal Control + Ende-zu-Ende Längsregelung + + + openpilot longitudinal control may come in a future update. + Die openpilot Längsregelung könnte in einem zukünftigen Update verfügbar sein. + + + An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + Eine Alpha-Version der openpilot Längsregelung kann zusammen mit dem Experimentellen Modus auf non-stable Branches getestet werden. + + + Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. + Aktiviere den Schalter für openpilot Längsregelung (Alpha), um den Experimentellen Modus zu erlauben. + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + Standard wird empfohlen. Im aggressiven Modus folgt openpilot vorausfahrenden Fahrzeugen enger und ist beim Gasgeben und Bremsen aggressiver. Im entspannten Modus hält openpilot mehr Abstand zu vorausfahrenden Fahrzeugen. Bei unterstützten Fahrzeugen kannst du mit der Abstandstaste am Lenkrad zwischen diesen Fahrstilen wechseln. + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + Die Fahrvisualisierung wechselt bei niedrigen Geschwindigkeiten auf die nach vorne gerichtete Weitwinkelkamera, um Kurven besser darzustellen. Das Logo des Experimentellen Modus wird außerdem oben rechts angezeigt. + + + Always-On Driver Monitoring + Dauerhaft aktive Fahrerüberwachung + + + Enable driver monitoring even when openpilot is not engaged. + Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist. + Updater @@ -1155,18 +1159,18 @@ This may take up to a minute. WiFiPromptWidget - - Open - - - - Maximize your training data uploads to improve openpilot's driving models. - - - - <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> - - + + Open + Öffnen + + + Maximize your training data uploads to improve openpilot's driving models. + Maximiere deine Trainingsdaten-Uploads, um die Fahrmodelle von openpilot zu verbessern. + + + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose-Modus <span style='font-family: Noto Color Emoji;'>🔥</span> + WifiUI From 2c162d9b75ae4e77bdf5bc3225e880d352ec0f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Thu, 17 Apr 2025 23:21:25 -0700 Subject: [PATCH 025/477] Tomb raider 2 (#35029) * db56b8fb-9135-4ab6-af18-99b7df7b2245/400 * fixes * linter unhappy * 6dbe0991-baa1-49ad-836a-ab370d1f0d92/400 * This one is good: 19387087-1005-475e-9015-9458dd8e7c5f/400 * Better every day: 39ed911c-0937-417f-97d2-58a8bb3caa53/400 * Actually end-to-end * typo * smooooooth: 94e23541-eb84-4fef-9f51-6a2d82aff314/360 * Revert "smooooooth: 94e23541-eb84-4fef-9f51-6a2d82aff314/360" This reverts commit edd4f02386d83d82dd8a188985cde80ed1646b7f. * 11632ef7-f555-489c-8480-e3bf97d9285e/400 * 08712d27-f6bd-4536-a30e-c729e5f62356/400 * 0a92a35e-1f72-476a-8cb6-c9f103f36822/400 * ee6d2394-2072-420c-a664-b4c0d4ed0b61/400 * no prev curv * No double work * fix bug * smooth * update prev action * whitespace * add little accel * new ref * Update plant.py --- .../controls/lib/longitudinal_planner.py | 20 ++++++++++++++----- selfdrive/modeld/fill_model_msg.py | 10 +++++----- selfdrive/modeld/modeld.py | 7 ++++--- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- selfdrive/modeld/parse_model_outputs.py | 13 ++++++------ .../test/longitudinal_maneuvers/plant.py | 1 + selfdrive/test/process_replay/ref_commit | 2 +- 8 files changed, 36 insertions(+), 25 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 8cbf0beee9..104c7c9d49 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -89,14 +89,15 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mpc.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' + self.mpc.mode = 'acc' + self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: accel_coast = get_coast_accel(sm['carControl'].orientationNED[1]) else: accel_coast = ACCEL_MAX - v_ego = sm['carState'].vEgo + v_ego = sm['modelV2'].velocity.x[0] v_cruise_kph = min(sm['carState'].vCruise, V_CRUISE_MAX) v_cruise = v_cruise_kph * CV.KPH_TO_MS v_cruise_initialized = sm['carState'].vCruise != V_CRUISE_UNSET @@ -112,7 +113,7 @@ class LongitudinalPlanner: # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - if self.mpc.mode == 'acc': + if self.mode == 'acc': accel_clip = [ACCEL_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP) @@ -128,7 +129,7 @@ class LongitudinalPlanner: self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego)) # Compute model v_ego error self.v_model_error = get_speed_error(sm['modelV2'], v_ego) - x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error) + x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], 0) # Don't clip at low speeds since throttle_prob doesn't account for creep self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED @@ -159,8 +160,17 @@ class LongitudinalPlanner: self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 action_t = self.CP.longitudinalActuatorDelay + DT_MDL - output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, + output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, action_t=action_t, vEgoStopping=self.CP.vEgoStopping) + output_a_target_e2e = sm['modelV2'].action.desiredAcceleration + output_should_stop_e2e = sm['modelV2'].action.shouldStop + + if self.mode == 'acc': + output_a_target = output_a_target_mpc + self.output_should_stop = output_should_stop_mpc + else: + output_a_target = min(output_a_target_mpc, output_a_target_e2e) + self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc for idx in range(2): accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05) diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index a91c6395c7..36bd724d01 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -90,11 +90,11 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T) # temporal pose - temporal_pose = modelV2.temporalPose - temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() - temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() - temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() - temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() + #temporal_pose = modelV2.temporalPose + #temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() + #temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() + #temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() + #temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() # poly path fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index f5007273ec..0ad629f519 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -43,8 +43,8 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' -LAT_SMOOTH_SECONDS = 0.0 -LONG_SMOOTH_SECONDS = 0.0 +LAT_SMOOTH_SECONDS = 0.3 +LONG_SMOOTH_SECONDS = 0.3 def smooth_value(val, prev_val, tau): alpha = 1 - np.exp(-DT_MDL / tau) if tau > 0 else 1 @@ -174,7 +174,7 @@ class ModelState: # TODO model only uses last value now self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] - self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs] + self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} if SEND_RAW_PRED: @@ -338,6 +338,7 @@ def main(demo=False): posenet_send = messaging.new_message('cameraOdometry') action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL) + prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 3601bbb5da..3bf2e01b5e 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36 -size 15966721 +oid sha256:fa00a07a4307b0f1638a10ec33b651d8a102e38371289b744f43215c89dfd342 +size 15578328 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index aee6d8f1bf..5e08b78472 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624 -size 34882971 +oid sha256:f222d2c528f1763828de01bb55e8979b1e4056e1dbb41350f521d2d2bb09d177 +size 46265585 diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 810c44ccb9..783572d436 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -88,6 +88,12 @@ class Parser: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + for k in ['lead_prob', 'lane_lines_prob']: + self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) return outs @@ -95,17 +101,10 @@ class Parser: def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) if 'lat_planner_solution' in outs: self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) if 'desired_curvature' in outs: self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) - for k in ['lead_prob', 'lane_lines_prob']: - self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py index 989b84dee3..b8c6adb436 100755 --- a/selfdrive/test/longitudinal_maneuvers/plant.py +++ b/selfdrive/test/longitudinal_maneuvers/plant.py @@ -107,6 +107,7 @@ class Plant: position = log.XYZTData.new_message() position.x = [float(x) for x in (self.speed + 0.5) * np.array(ModelConstants.T_IDXS)] model.modelV2.position = position + model.modelV2.action.desiredAcceleration = float(self.acceleration + 0.1) velocity = log.XYZTData.new_message() velocity.x = [float(x) for x in (self.speed + 0.5) * np.ones_like(ModelConstants.T_IDXS)] velocity.x[0] = float(self.speed) # always start at current speed diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 632922a8a4..5f89d7802d 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -3d3f875dc211ab0ca8a8e564ecf6dd3f52fcf7d9 \ No newline at end of file +0800e73b5d9c801f161b9559557c0559b0682fec \ No newline at end of file From 65431f4e2f0a6835fa2f4a2a9d2dc5a0ce227e74 Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:02:50 -0700 Subject: [PATCH 026/477] DEC: adjust dynamic speed adaptation parameters (#813) * early and better * Update sunnypilot/selfdrive/controls/lib/dec/constants.py --------- Co-authored-by: Jason Wen Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> --- sunnypilot/selfdrive/controls/lib/dec/constants.py | 5 ++--- sunnypilot/selfdrive/controls/lib/dec/dec.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sunnypilot/selfdrive/controls/lib/dec/constants.py b/sunnypilot/selfdrive/controls/lib/dec/constants.py index c1e3726606..48f4203e93 100644 --- a/sunnypilot/selfdrive/controls/lib/dec/constants.py +++ b/sunnypilot/selfdrive/controls/lib/dec/constants.py @@ -2,12 +2,11 @@ class WMACConstants: LEAD_WINDOW_SIZE = 5 LEAD_PROB = 0.5 - SLOW_DOWN_WINDOW_SIZE = 4 + SLOW_DOWN_WINDOW_SIZE = 5 SLOW_DOWN_PROB = 0.6 SLOW_DOWN_BP = [0., 10., 20., 30., 40., 50., 55., 60.] - #SLOW_DOWN_DIST = [25., 38., 55., 75., 95., 115., 130., 150.] - SLOW_DOWN_DIST = [30., 45., 60., 80., 100., 120., 135., 150.] + SLOW_DOWN_DIST = [25., 38., 55., 75., 95., 115., 130., 150.] SLOWNESS_WINDOW_SIZE = 12 SLOWNESS_PROB = 0.5 diff --git a/sunnypilot/selfdrive/controls/lib/dec/dec.py b/sunnypilot/selfdrive/controls/lib/dec/dec.py index 0c33da500a..812e5cc15f 100644 --- a/sunnypilot/selfdrive/controls/lib/dec/dec.py +++ b/sunnypilot/selfdrive/controls/lib/dec/dec.py @@ -164,7 +164,7 @@ class DynamicExperimentalController: """ Adapts the slow-down threshold based on vehicle speed and recent behavior. """ - slowdown_scaling_factor: float = (1.0 + 0.05 * np.log(1 + len(self._slow_down_gmac.data))) + slowdown_scaling_factor: float = (1.0 + 0.03 * np.log(1 + len(self._slow_down_gmac.data))) adaptive_threshold: float = float( interp(self._v_ego_kph, WMACConstants.SLOW_DOWN_BP, WMACConstants.SLOW_DOWN_DIST) * slowdown_scaling_factor ) From 3b60b22ceeb090812e5fa948ac6e56442d681dc4 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Fri, 18 Apr 2025 14:29:37 -0700 Subject: [PATCH 027/477] OS04C10: use IFE downscaler for road cameras (#35023) * squashh * wrong * clean up * rename --- system/camerad/cameras/camera_common.cc | 3 -- system/camerad/cameras/camera_common.h | 2 +- system/camerad/cameras/camera_qcom2.cc | 10 ++-- system/camerad/cameras/ife.h | 18 ++++---- system/camerad/cameras/spectra.cc | 40 +++++++++------- system/camerad/sensors/os04c10.cc | 11 +++++ system/camerad/sensors/os04c10_registers.h | 54 ++++++++++++++-------- system/camerad/sensors/sensor.h | 2 + 8 files changed, 87 insertions(+), 53 deletions(-) diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index f6c2681dea..1f6ad9b4be 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -26,9 +26,6 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, SpectraCamera * LOGD("allocated %d CL buffers", frame_buf_count); } - out_img_width = sensor->frame_width; - out_img_height = sensor->hdr_offset > 0 ? (sensor->frame_height - sensor->hdr_offset) / 2 : sensor->frame_height; - // the encoder HW tells us the size it wants after setting it up. // TODO: VENUS_BUFFER_SIZE should give the size, but it's too small. dependent on encoder settings? size_t nv12_size = (out_img_width <= 1344 ? 2900 : 2346)*cam->stride; diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h index c0426678b6..c26859cbc4 100644 --- a/system/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -32,7 +32,7 @@ public: VisionBuf *cur_yuv_buf; VisionBuf *cur_camera_buf; std::unique_ptr camera_bufs_raw; - int out_img_width, out_img_height; + uint32_t out_img_width, out_img_height; CameraBuf() = default; ~CameraBuf(); diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 18a42f05d8..8c4602bb31 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -73,7 +73,7 @@ void CameraState::init(VisionIpcServer *v, cl_device_id device_id, cl_context ct if (!camera.enabled) return; - fl_pix = camera.cc.focal_len / camera.sensor->pixel_size_mm; + fl_pix = camera.cc.focal_len / camera.sensor->pixel_size_mm / camera.sensor->out_scale; set_exposure_rect(); dc_gain_weight = camera.sensor->dc_gain_min_weight; @@ -107,10 +107,10 @@ void CameraState::set_exposure_rect() { float fl_ref = ae_target.second; ae_xywh = (Rect){ - std::max(0, camera.buf.out_img_width / 2 - (int)(fl_pix / fl_ref * xywh_ref.w / 2)), - std::max(0, camera.buf.out_img_height / 2 - (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))), - std::min((int)(fl_pix / fl_ref * xywh_ref.w), camera.buf.out_img_width / 2 + (int)(fl_pix / fl_ref * xywh_ref.w / 2)), - std::min((int)(fl_pix / fl_ref * xywh_ref.h), camera.buf.out_img_height / 2 + (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))) + std::max(0, (int)camera.buf.out_img_width / 2 - (int)(fl_pix / fl_ref * xywh_ref.w / 2)), + std::max(0, (int)camera.buf.out_img_height / 2 - (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))), + std::min((int)(fl_pix / fl_ref * xywh_ref.w), (int)camera.buf.out_img_width / 2 + (int)(fl_pix / fl_ref * xywh_ref.w / 2)), + std::min((int)(fl_pix / fl_ref * xywh_ref.h), (int)camera.buf.out_img_height / 2 + (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))) }; } diff --git a/system/camerad/cameras/ife.h b/system/camerad/cameras/ife.h index 49737f2db7..fd87d2baa4 100644 --- a/system/camerad/cameras/ife.h +++ b/system/camerad/cameras/ife.h @@ -105,7 +105,7 @@ int build_update(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std: } -int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std::vector &patches) { +int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std::vector &patches, uint32_t out_width, uint32_t out_height) { uint8_t *start = dst; // start with the every frame config @@ -185,12 +185,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo // output size/scaling dst += write_cont(dst, 0xa3c, { 0x00000003, - ((s->frame_width - 1) << 16) | (s->frame_width - 1), + ((out_width - 1) << 16) | (s->frame_width - 1), 0x30036666, 0x00000000, 0x00000000, s->frame_width - 1, - ((s->frame_height - 1) << 16) | (s->frame_height - 1), + ((out_height - 1) << 16) | (s->frame_height - 1), 0x30036666, 0x00000000, 0x00000000, @@ -198,12 +198,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo }); dst += write_cont(dst, 0xa68, { 0x00000003, - ((s->frame_width/2 - 1) << 16) | (s->frame_width - 1), + ((out_width / 2 - 1) << 16) | (s->frame_width - 1), 0x3006cccc, 0x00000000, 0x00000000, s->frame_width - 1, - ((s->frame_height/2 - 1) << 16) | (s->frame_height - 1), + ((out_height / 2 - 1) << 16) | (s->frame_height - 1), 0x3006cccc, 0x00000000, 0x00000000, @@ -212,12 +212,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo // cropping dst += write_cont(dst, 0xe10, { - s->frame_height - 1, - s->frame_width - 1, + out_height - 1, + out_width - 1, }); dst += write_cont(dst, 0xe30, { - s->frame_height/2 - 1, - s->frame_width - 1, + out_height / 2 - 1, + out_width - 1, }); dst += write_cont(dst, 0xe18, { 0x0ff00000, diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc index 4535078771..47ae9061f4 100644 --- a/system/camerad/cameras/spectra.cc +++ b/system/camerad/cameras/spectra.cc @@ -281,18 +281,21 @@ void SpectraCamera::camera_open(VisionIpcServer *v, cl_device_id device_id, cl_c if (!enabled) return; + buf.out_img_width = sensor->frame_width / sensor->out_scale; + buf.out_img_height = (sensor->hdr_offset > 0 ? (sensor->frame_height - sensor->hdr_offset) / 2 : sensor->frame_height) / sensor->out_scale; + // size is driven by all the HW that handles frames, // the video encoder has certain alignment requirements in this case - stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, sensor->frame_width); - y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, sensor->frame_height); - uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, sensor->frame_height); + stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, buf.out_img_width); + y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, buf.out_img_height); + uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, buf.out_img_height); uv_offset = stride*y_height; yuv_size = uv_offset + stride*uv_height; if (cc.output_type != ISP_RAW_OUTPUT) { uv_offset = ALIGNED_SIZE(uv_offset, 0x1000); yuv_size = uv_offset + ALIGNED_SIZE(stride*uv_height, 0x1000); } - assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, sensor->frame_width)); + assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, buf.out_img_width)); assert(y_height/2 == uv_height); open = true; @@ -645,14 +648,14 @@ void SpectraCamera::config_bps(int idx, int request_id) { io_cfg[1].mem_handle[0] = buf_handle_yuv[idx]; io_cfg[1].mem_handle[1] = buf_handle_yuv[idx]; io_cfg[1].planes[0] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height, + .width = buf.out_img_width, + .height = buf.out_img_height, .plane_stride = stride, .slice_height = y_height, }; io_cfg[1].planes[1] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height/2, + .width = buf.out_img_width, + .height = buf.out_img_height / 2, .plane_stride = stride, .slice_height = uv_height, }; @@ -737,7 +740,7 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { bool is_raw = cc.output_type != ISP_IFE_PROCESSED; if (!is_raw) { if (init) { - buf_desc[0].length = build_initial_config((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches); + buf_desc[0].length = build_initial_config((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches, buf.out_img_width, buf.out_img_height); } else { buf_desc[0].length = build_update((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches); } @@ -844,14 +847,14 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { io_cfg[0].mem_handle[0] = buf_handle_yuv[idx]; io_cfg[0].mem_handle[1] = buf_handle_yuv[idx]; io_cfg[0].planes[0] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height, + .width = buf.out_img_width, + .height = buf.out_img_height, .plane_stride = stride, .slice_height = y_height, }; io_cfg[0].planes[1] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height/2, + .width = buf.out_img_width, + .height = buf.out_img_height / 2, .plane_stride = stride, .slice_height = uv_height, }; @@ -993,6 +996,9 @@ bool SpectraCamera::openSensor() { LOGD("-- Probing sensor %d", cc.camera_num); auto init_sensor_lambda = [this](SensorInfo *s) { + if (s->image_sensor == cereal::FrameData::ImageSensor::OS04C10 && cc.output_type == ISP_IFE_PROCESSED) { + ((OS04C10*)s)->ife_downscale_configure(); + } sensor.reset(s); return (sensors_init() == 0); }; @@ -1065,8 +1071,8 @@ void SpectraCamera::configISP() { .data[0] = (struct cam_isp_out_port_info){ .res_type = CAM_ISP_IFE_OUT_RES_FULL, .format = CAM_FORMAT_NV12, - .width = sensor->frame_width, - .height = sensor->frame_height + sensor->extra_height, + .width = buf.out_img_width, + .height = buf.out_img_height + sensor->extra_height, .comp_grp_id = 0x0, .split_point = 0x0, .secure_mode = 0x0, }, }; @@ -1141,8 +1147,8 @@ void SpectraCamera::configICP() { }, .out_res[0] = (struct cam_icp_res_info){ .format = 0x3, // YUV420NV12 - .width = sensor->frame_width, - .height = sensor->frame_height, + .width = buf.out_img_width, + .height = buf.out_img_height, .fps = 20, }, }; diff --git a/system/camerad/sensors/os04c10.cc b/system/camerad/sensors/os04c10.cc index d008e1d07b..38be4ecca4 100644 --- a/system/camerad/sensors/os04c10.cc +++ b/system/camerad/sensors/os04c10.cc @@ -20,6 +20,17 @@ const uint32_t os04c10_analog_gains_reg[] = { } // namespace +void OS04C10::ife_downscale_configure() { + out_scale = 2; + + pixel_size_mm = 0.002; + frame_width = 2688; + frame_height = 1520; + exposure_time_max = 2352; + + init_reg_array.insert(init_reg_array.end(), std::begin(ife_downscale_override_array_os04c10), std::end(ife_downscale_override_array_os04c10)); +} + OS04C10::OS04C10() { image_sensor = cereal::FrameData::ImageSensor::OS04C10; bayer_pattern = CAM_ISP_PATTERN_BAYER_BGBGBG; diff --git a/system/camerad/sensors/os04c10_registers.h b/system/camerad/sensors/os04c10_registers.h index 8b1c78c69d..b501656090 100644 --- a/system/camerad/sensors/os04c10_registers.h +++ b/system/camerad/sensors/os04c10_registers.h @@ -88,8 +88,6 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37c7, 0xa8}, {0x37da, 0x11}, {0x381f, 0x08}, - // {0x3829, 0x03}, - // {0x3832, 0x00}, {0x3881, 0x00}, {0x3888, 0x04}, {0x388b, 0x00}, @@ -199,7 +197,6 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x370b, 0x48}, {0x370c, 0x01}, {0x370f, 0x00}, - {0x3714, 0x28}, {0x3716, 0x04}, {0x3719, 0x11}, {0x371a, 0x1e}, @@ -231,7 +228,6 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37bd, 0x01}, {0x37bf, 0x26}, {0x37c0, 0x11}, - {0x37c2, 0x14}, {0x37cd, 0x19}, {0x37e0, 0x08}, {0x37e6, 0x04}, @@ -241,14 +237,9 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37d8, 0x02}, {0x37e2, 0x10}, {0x3739, 0x10}, - {0x3662, 0x08}, {0x37e4, 0x20}, {0x37e3, 0x08}, - {0x37d9, 0x04}, {0x4040, 0x00}, - {0x4041, 0x03}, - {0x4008, 0x01}, - {0x4009, 0x06}, // FSIN {0x3002, 0x22}, @@ -269,20 +260,11 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x8f}, {0x3806, 0x05}, {0x3807, 0xff}, - {0x3808, 0x05}, {0x3809, 0x40}, - {0x380a, 0x02}, {0x380b, 0xf8}, {0x3811, 0x08}, {0x3813, 0x08}, - {0x3814, 0x03}, {0x3815, 0x01}, - {0x3816, 0x03}, {0x3817, 0x01}, - {0x380c, 0x0b}, {0x380d, 0xac}, // HTS - {0x380e, 0x06}, {0x380f, 0x9c}, // VTS - - {0x3820, 0xb3}, - {0x3821, 0x01}, {0x3880, 0x00}, {0x3882, 0x20}, {0x3c91, 0x0b}, @@ -331,4 +313,40 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { // r {0x5104, 0x08}, {0x5105, 0xd6}, {0x5144, 0x08}, {0x5145, 0xd6}, + + {0x3714, 0x28}, + {0x37c2, 0x14}, + {0x3662, 0x08}, + {0x37d9, 0x04}, + {0x4041, 0x03}, + {0x4008, 0x01}, + {0x4009, 0x06}, + {0x3808, 0x05}, {0x3809, 0x40}, + {0x380a, 0x02}, {0x380b, 0xf8}, + {0x3814, 0x03}, + {0x3816, 0x03}, + {0x380c, 0x0b}, {0x380d, 0xac}, // HTS + {0x380e, 0x06}, {0x380f, 0x9c}, // VTS + {0x3820, 0xb3}, + {0x3821, 0x01}, +}; + +const struct i2c_random_wr_payload ife_downscale_override_array_os04c10[] = { + // OS04C10_AA_00_02_17_wAO_2688x1524_MIPI728Mbps_Linear12bit_20FPS_4Lane_MCLK24MHz + {0x3829, 0x03}, + {0x3714, 0x24}, + {0x37c2, 0x04}, + {0x3662, 0x10}, + {0x37d9, 0x08}, + {0x4041, 0x07}, + {0x4008, 0x02}, + {0x4009, 0x0d}, + {0x3808, 0x0a}, {0x3809, 0x80}, + {0x380a, 0x05}, {0x380b, 0xf0}, + {0x3814, 0x01}, + {0x3816, 0x01}, + {0x380c, 0x08}, {0x380d, 0x5c}, // HTS + {0x380e, 0x09}, {0x380f, 0x38}, // VTS + {0x3820, 0xb0}, + {0x3821, 0x00}, }; diff --git a/system/camerad/sensors/sensor.h b/system/camerad/sensors/sensor.h index c1131aafdf..d4be3cf036 100644 --- a/system/camerad/sensors/sensor.h +++ b/system/camerad/sensors/sensor.h @@ -29,6 +29,7 @@ public: uint32_t frame_stride; uint32_t frame_offset = 0; uint32_t extra_height = 0; + int out_scale = 1; int registers_offset = -1; int stats_offset = -1; int hdr_offset = -1; @@ -109,6 +110,7 @@ public: class OS04C10 : public SensorInfo { public: OS04C10(); + void ife_downscale_configure(); std::vector getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const override; float getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const override; int getSlaveAddress(int port) const override; From 7ceb284d6d3557713ec58f4726f864a0e5f1ebaa Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Fri, 18 Apr 2025 19:21:52 -0700 Subject: [PATCH 028/477] process replay migration: fix longitudinalPlan (#35035) --- selfdrive/test/process_replay/migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/test/process_replay/migration.py b/selfdrive/test/process_replay/migration.py index 39d5452b6b..02cd9b6d71 100644 --- a/selfdrive/test/process_replay/migration.py +++ b/selfdrive/test/process_replay/migration.py @@ -13,7 +13,7 @@ from opendbc.car.gm.values import GMSafetyFlags from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.fill_model_msg import fill_xyz_poly, fill_lane_line_meta from openpilot.selfdrive.test.process_replay.vision_meta import meta_from_encode_index -from openpilot.selfdrive.controls.lib.longitudinal_planner import get_accel_from_plan +from openpilot.selfdrive.controls.lib.longitudinal_planner import get_accel_from_plan, CONTROL_N_T_IDX from openpilot.system.manager.process_config import managed_processes from openpilot.tools.lib.logreader import LogIterable @@ -109,7 +109,7 @@ def migrate_longitudinalPlan(msgs): if msg.which() != 'longitudinalPlan': continue new_msg = msg.as_builder() - a_target, should_stop = get_accel_from_plan(msg.longitudinalPlan.speeds, msg.longitudinalPlan.accels) + a_target, should_stop = get_accel_from_plan(msg.longitudinalPlan.speeds, msg.longitudinalPlan.accels, CONTROL_N_T_IDX) new_msg.longitudinalPlan.aTarget, new_msg.longitudinalPlan.shouldStop = float(a_target), bool(should_stop) ops.append((index, new_msg.as_reader())) return ops, [], [] From 1db91d6257a21c428eb1851458a5bff02194ab45 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 19 Apr 2025 07:53:20 +0200 Subject: [PATCH 029/477] ci: ui preview add unicode characters and emojis (#835) Add unicode characters and emojis! --- Dockerfile.openpilot_base | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile.openpilot_base b/Dockerfile.openpilot_base index 44d8d95e95..04fb589bf6 100644 --- a/Dockerfile.openpilot_base +++ b/Dockerfile.openpilot_base @@ -60,6 +60,7 @@ ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute ENV QTWEBENGINE_DISABLE_SANDBOX=1 RUN dbus-uuidgen > /etc/machine-id +RUN apt-get update && apt-get install -y fonts-noto-cjk fonts-noto-color-emoji ARG USER=batman ARG USER_UID=1001 From fb5fa70e78c18566ce79ebdb554d02ceec07d176 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 19 Apr 2025 16:17:18 +0100 Subject: [PATCH 030/477] ui(raylib): update text window font, new lines and indentation (#35031) --- system/ui/text.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/system/ui/text.py b/system/ui/text.py index 97d0ebcce9..4ea886ba1e 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import re import pyray as rl from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.button import gui_button, ButtonStyle @@ -7,8 +8,8 @@ from openpilot.system.ui.lib.application import gui_app MARGIN = 50 SPACING = 50 -FONT_SIZE = 60 -LINE_HEIGHT = 64 +FONT_SIZE = 72 +LINE_HEIGHT = 80 BUTTON_SIZE = rl.Vector2(310, 160) DEMO_TEXT = """This is a sample text that will be wrapped and scrolled if necessary. @@ -16,18 +17,24 @@ DEMO_TEXT = """This is a sample text that will be wrapped and scrolled if necess def wrap_text(text, font_size, max_width): lines = [] - current_line = "" font = gui_app.font() - for word in text.split(): - test_line = current_line + word + " " - if rl.measure_text_ex(font, test_line, font_size, 0).x <= max_width: - current_line = test_line - else: + for paragraph in text.split("\n"): + if not paragraph.strip(): + lines.append("") + continue + indent = re.match(r"^\s*", paragraph).group() + current_line = indent + for word in paragraph.split(): + test_line = current_line + word + " " + if rl.measure_text_ex(font, test_line, font_size, 0).x <= max_width: + current_line = test_line + else: + lines.append(current_line) + current_line = word + " " + current_line = current_line.rstrip() + if current_line: lines.append(current_line) - current_line = word + " " - if current_line: - lines.append(current_line) return lines @@ -45,7 +52,7 @@ class TextWindow: position = rl.Vector2(self._textarea_rect.x + scroll.x, self._textarea_rect.y + scroll.y + i * LINE_HEIGHT) if position.y + LINE_HEIGHT < self._textarea_rect.y or position.y > self._textarea_rect.y + self._textarea_rect.height: continue - rl.draw_text_ex(gui_app.font(), line.strip(), position, FONT_SIZE, 0, rl.WHITE) + rl.draw_text_ex(gui_app.font(), line, position, FONT_SIZE, 0, rl.WHITE) rl.end_scissor_mode() button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) From bcdb5466380e9e789afaa9b03620607c11d6719c Mon Sep 17 00:00:00 2001 From: Tim Wilson Date: Sat, 19 Apr 2025 09:47:47 -0600 Subject: [PATCH 031/477] NNLC: decreased low-speed factor (#822) * NNLC: decreased low-speed factor * np.float to float * format * add tests for sanity check --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen --- selfdrive/controls/lib/latcontrol_torque.py | 3 +- .../controls/lib/latcontrol_torque_ext.py | 5 +- .../lib/latcontrol_torque_ext_base.py | 2 + .../selfdrive/controls/lib/nnlc/nnlc.py | 7 +++ .../controls/lib/nnlc/tests/test_nnlc.py | 56 +++++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 613824a4ed..5edf4cf91b 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -80,7 +80,8 @@ class LatControlTorque(LatControl): # Lateral acceleration torque controller extension updates # Overrides stock ff and pid_log.error ff, pid_log = self.extension.update(CS, VM, params, ff, pid_log, setpoint, measurement, calibrated_pose, roll_compensation, - desired_lateral_accel, actual_lateral_accel, lateral_accel_deadzone, gravity_adjusted_lateral_accel) + desired_lateral_accel, actual_lateral_accel, lateral_accel_deadzone, gravity_adjusted_lateral_accel, + desired_curvature, actual_curvature) freeze_integrator = steer_limited_by_controls or CS.steeringPressed or CS.vEgo < 5 output_torque = self.pid.update(pid_log.error, diff --git a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext.py b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext.py index ee8d64dcd0..fe54f46ca7 100644 --- a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext.py +++ b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext.py @@ -13,7 +13,8 @@ class LatControlTorqueExt(NeuralNetworkLateralControl): super().__init__(lac_torque, CP, CP_SP) def update(self, CS, VM, params, ff, pid_log, setpoint, measurement, calibrated_pose, roll_compensation, - desired_lateral_accel, actual_lateral_accel, lateral_accel_deadzone, gravity_adjusted_lateral_accel): + desired_lateral_accel, actual_lateral_accel, lateral_accel_deadzone, gravity_adjusted_lateral_accel, + desired_curvature, actual_curvature): self._ff = ff self._pid_log = pid_log self._setpoint = setpoint @@ -21,6 +22,8 @@ class LatControlTorqueExt(NeuralNetworkLateralControl): self._lateral_accel_deadzone = lateral_accel_deadzone self._desired_lateral_accel = desired_lateral_accel self._actual_lateral_accel = actual_lateral_accel + self._desired_curvature = desired_curvature + self._actual_curvature = actual_curvature self.update_calculations(CS, VM, desired_lateral_accel) self.update_neural_network_feedforward(CS, params, calibrated_pose) diff --git a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py index 48d98501b2..800bbac67a 100644 --- a/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py +++ b/sunnypilot/selfdrive/controls/lib/latcontrol_torque_ext_base.py @@ -62,6 +62,8 @@ class LatControlTorqueExtBase: self._lateral_accel_deadzone = 0.0 self._desired_lateral_accel = 0.0 self._actual_lateral_accel = 0.0 + self._desired_curvature = 0.0 + self._actual_curvature = 0.0 # twilsonco's Lateral Neural Network Feedforward # Instantaneous lateral jerk changes very rapidly, making it not useful on its own, diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py b/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py index aee359574b..ba4285ba2b 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py @@ -16,6 +16,9 @@ from openpilot.sunnypilot.selfdrive.controls.lib.latcontrol_torque_ext_base impo from openpilot.sunnypilot.selfdrive.controls.lib.nnlc.helpers import MOCK_MODEL_PATH from openpilot.sunnypilot.selfdrive.controls.lib.nnlc.model import NNTorqueModel +LOW_SPEED_X = [0, 10, 20, 30] +LOW_SPEED_Y = [12, 3, 1, 0] + # At a given roll, if pitch magnitude increases, the # gravitational acceleration component starts pointing @@ -59,6 +62,10 @@ class NeuralNetworkLateralControl(LatControlTorqueExtBase): if not self.enabled or not self.model_valid or not self.has_nn_model: return + low_speed_factor = float(np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)) ** 2 + self._setpoint = self._desired_lateral_accel + low_speed_factor * self._desired_curvature + self._measurement = self._actual_lateral_accel + low_speed_factor * self._actual_curvature + # update past data roll = params.roll if calibrated_pose is not None: diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py new file mode 100644 index 0000000000..c7227c2d51 --- /dev/null +++ b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py @@ -0,0 +1,56 @@ +from parameterized import parameterized + +from cereal import car, log +from opendbc.car.car_helpers import interfaces +from opendbc.car.honda.values import CAR as HONDA +from opendbc.car.hyundai.values import CAR as HYUNDAI +from opendbc.car.toyota.values import CAR as TOYOTA +from opendbc.car.vehicle_model import VehicleModel +from openpilot.common.params import Params +from openpilot.selfdrive.car.helpers import convert_to_capnp +from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque +from openpilot.selfdrive.locationd.helpers import Pose +from openpilot.common.mock.generators import generate_livePose +from openpilot.sunnypilot.selfdrive.car import interfaces as sunnypilot_interfaces + + +class TestNeuralNetworkLateralControl: + + @parameterized.expand([HONDA.HONDA_CIVIC, TOYOTA.TOYOTA_RAV4, HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN]) + def test_saturation(self, car_name): + params = Params() + params.put_bool("NeuralNetworkLateralControl", True) + + CarInterface = interfaces[car_name] + CP = CarInterface.get_non_essential_params(car_name) + CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name) + CI = CarInterface(CP, CP_SP) + + sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params) + + CP_SP = convert_to_capnp(CP_SP) + VM = VehicleModel(CP) + + controller = LatControlTorque(CP.as_reader(), CP_SP.as_reader(), CI) + + CS = car.CarState.new_message() + CS.vEgo = 30 + CS.steeringPressed = False + + params = log.LiveParametersData.new_message() + + lp = generate_livePose() + pose = Pose.from_live_pose(lp.livePose) + + # Saturate for curvature limited and controller limited + for _ in range(1000): + _, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True) + assert lac_log.saturated + + for _ in range(1000): + _, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False) + assert not lac_log.saturated + + for _ in range(1000): + _, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False) + assert lac_log.saturated From 618b7ccf230d4048f85924c7b1bbd0f9f1b6533a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 19 Apr 2025 17:08:33 +0100 Subject: [PATCH 032/477] ui(raylib): calculate spinner progress in set_text (#35036) --- system/ui/spinner.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 951c6f697e..edf1fe5ca9 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -26,11 +26,17 @@ class Spinner: self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE) self._rotation = 0.0 self._text: str = "" + self._progress: int | None = None self._lock = threading.Lock() def set_text(self, text: str) -> None: with self._lock: - self._text = text + if text.isdigit(): + self._progress = clamp(int(text), 0, 100) + self._text = "" + else: + self._progress = None + self._text = text def render(self): center = rl.Vector2(gui_app.width / 2.0, gui_app.height / 2.0) @@ -49,23 +55,21 @@ class Spinner: rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE) # Display progress bar or text based on user input - text = None + y_pos = rl.get_screen_height() - MARGIN - PROGRESS_BAR_HEIGHT with self._lock: + progress = self._progress text = self._text - if text: - y_pos = rl.get_screen_height() - MARGIN - PROGRESS_BAR_HEIGHT - if text.isdigit(): - progress = clamp(int(text), 0, 100) - bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT) - rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY) + if progress is not None: + bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT) + rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY) - bar.width *= progress / 100.0 - rl.draw_rectangle_rounded(bar, 1, 10, rl.WHITE) - else: - text_size = rl.measure_text_ex(gui_app.font(), text, FONT_SIZE, 1.0) - rl.draw_text_ex(gui_app.font(), text, - rl.Vector2(center.x - text_size.x / 2, y_pos), FONT_SIZE, 1.0, rl.WHITE) + bar.width *= progress / 100.0 + rl.draw_rectangle_rounded(bar, 1, 10, rl.WHITE) + elif text: + text_size = rl.measure_text_ex(gui_app.font(), text, FONT_SIZE, 1.0) + rl.draw_text_ex(gui_app.font(), text, + rl.Vector2(center.x - text_size.x / 2, y_pos), FONT_SIZE, 1.0, rl.WHITE) if __name__ == "__main__": From 1bf752fc79611a9a73876ad5d5dfb50adce4e26f Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Sat, 19 Apr 2025 12:15:28 -0700 Subject: [PATCH 033/477] bugfix: Auto lane change - Add mode to disable lane changes (#826) * off is off * sugg * line * Update sunnypilot/selfdrive/controls/lib/tests/test_auto_lane_change.py --------- Co-authored-by: Jason Wen Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: DevTekVE --- selfdrive/controls/lib/desire_helper.py | 4 ++-- .../controls/lib/tests/test_auto_lane_change.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/lib/desire_helper.py b/selfdrive/controls/lib/desire_helper.py index 34ea4ff1bb..23386e2ad7 100644 --- a/selfdrive/controls/lib/desire_helper.py +++ b/selfdrive/controls/lib/desire_helper.py @@ -1,7 +1,7 @@ from cereal import log from openpilot.common.conversions import Conversions as CV from openpilot.common.realtime import DT_MDL -from openpilot.sunnypilot.selfdrive.controls.lib.auto_lane_change import AutoLaneChangeController +from openpilot.sunnypilot.selfdrive.controls.lib.auto_lane_change import AutoLaneChangeController, AutoLaneChangeMode LaneChangeState = log.LaneChangeState LaneChangeDirection = log.LaneChangeDirection @@ -48,7 +48,7 @@ class DesireHelper: one_blinker = carstate.leftBlinker != carstate.rightBlinker below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN - if not lateral_active or self.lane_change_timer > LANE_CHANGE_TIME_MAX: + if not lateral_active or self.lane_change_timer > LANE_CHANGE_TIME_MAX or self.alc.lane_change_set_timer == AutoLaneChangeMode.OFF: self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none else: diff --git a/sunnypilot/selfdrive/controls/lib/tests/test_auto_lane_change.py b/sunnypilot/selfdrive/controls/lib/tests/test_auto_lane_change.py index c1b3053207..5d1fa360ed 100644 --- a/sunnypilot/selfdrive/controls/lib/tests/test_auto_lane_change.py +++ b/sunnypilot/selfdrive/controls/lib/tests/test_auto_lane_change.py @@ -51,6 +51,7 @@ class TestAutoLaneChangeController: assert not self.alc.prev_brake_pressed @parameterized.expand([(AutoLaneChangeMode.OFF, ), (AutoLaneChangeMode.NUDGE, )]) + def test_off_and_nudge_mode(self, timer_state): """Test the default OFF and NUDGE mode behavior.""" self._reset_states() @@ -196,3 +197,17 @@ class TestAutoLaneChangeController: self.alc.update_lane_change(blindspot_detected=False, brake_pressed=False) assert not self.alc.auto_lane_change_allowed + + def test_auto_lane_change_mode_off_disallows_lane_change(self): + """Test that OFF mode never allows auto lane change.""" + self._reset_states() + self.alc.lane_change_bsm_delay = False + self.alc.lane_change_set_timer = AutoLaneChangeMode.OFF + + # Simulate updates for a long period of time (e.g., 10 seconds) + num_updates = int(10.0 / DT_MDL) + for _ in range(num_updates): + self.alc.update_lane_change(blindspot_detected=False, brake_pressed=False) + + # Lane change should never be allowed + assert not self.alc.auto_lane_change_allowed From 2fe3c2748edfa862184227bc21554508daf03b85 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 19 Apr 2025 21:41:11 -0400 Subject: [PATCH 034/477] NNLC: use `safe_exp` to prevent overflow in `sigmoid` (#836) * test * prevent overflowing * unused --- .../selfdrive/controls/lib/nnlc/model.py | 4 ++- .../controls/lib/nnlc/tests/test_nnlc.py | 35 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/model.py b/sunnypilot/selfdrive/controls/lib/nnlc/model.py index 1e527c7370..ab6706bf36 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/model.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/model.py @@ -7,6 +7,8 @@ See the LICENSE.md file in the root directory for more details. from json import load import numpy as np +from openpilot.selfdrive.modeld.parse_model_outputs import safe_exp + # dict used to rename activation functions whose names aren't valid python identifiers ACTIVATION_FUNCTION_NAMES = {'σ': 'sigmoid'} @@ -40,7 +42,7 @@ class NNTorqueModel: # These are called by name using the keys in the model json file @staticmethod def sigmoid(x): - return 1 / (1 + np.exp(-x)) + return 1 / (1 + safe_exp(-x)) @staticmethod def identity(x): diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py index c7227c2d51..4e2649f2ff 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py @@ -1,6 +1,7 @@ +import numpy as np from parameterized import parameterized -from cereal import car, log +from cereal import car, log, messaging from opendbc.car.car_helpers import interfaces from opendbc.car.honda.values import CAR as HONDA from opendbc.car.hyundai.values import CAR as HYUNDAI @@ -12,6 +13,30 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.locationd.helpers import Pose from openpilot.common.mock.generators import generate_livePose from openpilot.sunnypilot.selfdrive.car import interfaces as sunnypilot_interfaces +from openpilot.selfdrive.modeld.constants import ModelConstants + + +def generate_modelV2(): + model = messaging.new_message('modelV2') + position = log.XYZTData.new_message() + speed = 30 + position.x = [float(x) for x in (speed + 0.5) * np.array(ModelConstants.T_IDXS)] + model.modelV2.position = position + orientation = log.XYZTData.new_message() + curvature = 0.05 + orientation.x = [float(curvature) for _ in ModelConstants.T_IDXS] + orientation.y = [0.0 for _ in ModelConstants.T_IDXS] + model.modelV2.orientation = orientation + velocity = log.XYZTData.new_message() + velocity.x = [float(x) for x in (speed + 0.5) * np.ones_like(ModelConstants.T_IDXS)] + velocity.x[0] = float(speed) # always start at current speed + model.modelV2.velocity = velocity + acceleration = log.XYZTData.new_message() + acceleration.x = [float(x) for x in np.zeros_like(ModelConstants.T_IDXS)] + acceleration.y = [float(y) for y in np.zeros_like(ModelConstants.T_IDXS)] + model.modelV2.acceleration = acceleration + + return model class TestNeuralNetworkLateralControl: @@ -42,15 +67,23 @@ class TestNeuralNetworkLateralControl: lp = generate_livePose() pose = Pose.from_live_pose(lp.livePose) + mdl = generate_modelV2() + sm = {'modelV2': mdl.modelV2} + model_v2 = sm['modelV2'] + controller.extension.model_v2 = model_v2 + # Saturate for curvature limited and controller limited for _ in range(1000): + controller.extension.update_model_v2(model_v2) _, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True) assert lac_log.saturated for _ in range(1000): + controller.extension.update_model_v2(model_v2) _, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False) assert not lac_log.saturated for _ in range(1000): + controller.extension.update_model_v2(model_v2) _, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False) assert lac_log.saturated From a87986dd825b62a19b195c5459321b35ad512a7c Mon Sep 17 00:00:00 2001 From: Tim Wilson Date: Sat, 19 Apr 2025 19:53:57 -0600 Subject: [PATCH 035/477] NNLC: fix weak torque in high lat-accel turns (#823) * NNLC: fix weak torque in high lat-accel turns * np.float to float --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen --- sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py b/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py index ba4285ba2b..218bbb9f6a 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/nnlc.py @@ -12,7 +12,7 @@ from opendbc.car.interfaces import LatControlInputs from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.selfdrive.modeld.constants import ModelConstants -from openpilot.sunnypilot.selfdrive.controls.lib.latcontrol_torque_ext_base import LatControlTorqueExtBase +from openpilot.sunnypilot.selfdrive.controls.lib.latcontrol_torque_ext_base import LatControlTorqueExtBase, sign from openpilot.sunnypilot.selfdrive.controls.lib.nnlc.helpers import MOCK_MODEL_PATH from openpilot.sunnypilot.selfdrive.controls.lib.nnlc.model import NNTorqueModel @@ -99,6 +99,22 @@ class NeuralNetworkLateralControl(LatControlTorqueExtBase): torque_from_measurement = self.model.evaluate(nnff_measurement_input) self._pid_log.error = torque_from_setpoint - torque_from_measurement + # The "pure" NNLC error response can be too weak for cars whose models were trained + # with a lack of high-magnitude lateral acceleration data, for which the NNLC model + # torque response flattens out at high lateral accelerations. + # This workaround blends in a guaranteed stronger error response only when the + # desired lateral acceleration is high enough to warrant it, by using the lateral acceleration + # error as the input to the NNLC model. This is not ideal, and potentially degrades the NNLC + # accuracy for cars that don't have this issue, but it's necessary until a better NNLC model + # structure is used that doesn't create this issue when high-magnitude data is missing. + error_blend_factor = float(np.interp(abs(self._desired_lateral_accel), [1.0, 2.0], [0.0, 1.0])) + if error_blend_factor > 0.0: # blend in stronger error response when in high lat accel + # NNFF inputs 5+ are optional, and if left out are replaced with 0.0 inside the NNFF class + nnff_error_input = [CS.vEgo, self._setpoint - self._measurement, self.lateral_jerk_setpoint - self.lateral_jerk_measurement, 0.0] + torque_from_error = self.model.evaluate(nnff_error_input) + if sign(self._pid_log.error) == sign(torque_from_error) and abs(self._pid_log.error) < abs(torque_from_error): + self._pid_log.error = self._pid_log.error * (1.0 - error_blend_factor) + torque_from_error * error_blend_factor + # compute feedforward (same as nn setpoint output) friction_input = self.update_friction_input(self._setpoint, self._measurement) nn_input = [CS.vEgo, self._desired_lateral_accel, friction_input, roll] \ From 4ea982ca37cbad859eb6d8561d7e363560b26fd2 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 20 Apr 2025 12:06:28 +0200 Subject: [PATCH 036/477] ci: trigger prebuilt builds via label from PR * ci: enhance pull request handling for prebuilt workflows * ci: add wait-for-action step to monitor selfdrive_tests workflow * ci: add validation job to monitor selfdrive_tests for prebuilt workflows * test * force negative condition to validate flow * force negative condition to validate flow * ci: add wait_for_tests input to control selfdrive_tests workflow execution * ci: update description for wait_for_tests input in workflow * Cleaning * Remove PR label when triggered by the label * Rename * Changing to target and only caring about labeled for the time being * gh action needs cancelled not canceled * Line ending --- .codespellignore | 1 + .../workflows/sunnypilot-build-prebuilt.yaml | 51 ++++++++++++++++--- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/.codespellignore b/.codespellignore index 6af9bb0f35..c7beb6f04f 100644 --- a/.codespellignore +++ b/.codespellignore @@ -1,3 +1,4 @@ Wen REGIST PullRequest +cancelled diff --git a/.github/workflows/sunnypilot-build-prebuilt.yaml b/.github/workflows/sunnypilot-build-prebuilt.yaml index c7c72919c5..241228598d 100644 --- a/.github/workflows/sunnypilot-build-prebuilt.yaml +++ b/.github/workflows/sunnypilot-build-prebuilt.yaml @@ -21,15 +21,30 @@ on: push: branches: [ master, master-new, master-dev-c3-new ] tags: [ '*' ] + pull_request_target: + types: [ labeled ] workflow_dispatch: inputs: - extra_version: - description: 'Extra version identifier' + wait_for_tests: + description: 'Wait for selfdrive_tests to finish' required: false - default: '' + type: boolean + default: false jobs: + validate_tests: + runs-on: ubuntu-24.04 + if: ((github.event_name == 'workflow_dispatch' && inputs.wait_for_tests) || contains('pull_request', github.event_name) && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) + steps: + - uses: actions/checkout@v4 + - name: Wait for Tests + uses: ./.github/workflows/wait-for-action # Path to where you place the action + with: + workflow: selfdrive_tests.yaml # The workflow file to monitor + github-token: ${{ secrets.GITHUB_TOKEN }} + build: + needs: [ validate_tests ] concurrency: group: build-${{ github.head_ref || github.ref_name }} cancel-in-progress: false @@ -39,6 +54,7 @@ jobs: version: ${{ steps.set-env.outputs.version }} extra_version_identifier: ${{ steps.set-env.outputs.extra_version_identifier }} commit_sha: ${{ steps.set-env.outputs.commit_sha }} + if: always() && !failure() && !cancelled() steps: - uses: actions/checkout@v4 with: @@ -194,10 +210,10 @@ jobs: concurrency: group: publish-${{ github.head_ref || github.ref_name }} cancel-in-progress: true - if: ${{ github.event_name != 'pull_request' || github.event_name == 'pull_request' && github.event.pull_request.draft }} + if: ${{ !contains('pull_request', github.event_name) || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt') }} needs: build runs-on: ubuntu-24.04 - environment: ${{ contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) && 'auto-deploy' || 'feature-branch' }} + environment: ${{ (contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) || contains(github.event.pull_request.labels.*.name, 'prebuilt')) && 'auto-deploy' || 'feature-branch' }} steps: - uses: actions/checkout@v4 @@ -268,4 +284,27 @@ jobs: echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/DEV_FEEDBACK_NOTIFICATION_BRANCHES" echo "2. Current value: ${{ vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES }}" echo "3. Update as needed (JSON array with no spaces)" - shell: alpine.sh {0} \ No newline at end of file + shell: alpine.sh {0} + + manage-pr-labels: + name: Remove prebuilt label + runs-on: ubuntu-latest + if: (always() && contains('pull_request', github.event_name) && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) + env: + LABEL: prebuilt + steps: + - name: Remove trust-fork-pr label if present + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.pull_request.number; + + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + name: process.env.LABEL + }); + + console.log(`Removed '${process.env.LABEL}' label from PR #${prNumber}`); From 898f30744f17a3abcb129921b48c7ab622bb431c Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 20 Apr 2025 13:04:35 +0200 Subject: [PATCH 037/477] ci: avoid deadlock on dev-c3 branch build when PR labeled dev-c3 (#839) * ci: enhance PR checks to validate individual check runs before merging and ignore reset-and-squash as candidate for fail * reset and squash script shouldn't will be cancelled only if a push to master has been made which always should take priority. The rest will be put on hold to avoid parallel squash scripts running * Set concurrencty at workflow level? * Set concurrencty at workflow level? * change trigger to pull_request to validate * Reducing the types of "pull request" events * Keep it as target * Playing eith run name a little * Change to pull request not target * simplifying * set to just pull request without target * maintain as pull request target --- .../sunnypilot-master-dev-c3-prep.yaml | 14 +++++--- release/ci/squash_and_merge_prs.py | 32 ++++++++++++++++--- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml index 1246b96780..7fdcb368ec 100644 --- a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml +++ b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml @@ -14,7 +14,7 @@ on: - master - master-new pull_request_target: - types: [ synchronize, opened, labeled ] + types: [ synchronize, labeled ] branches: - 'master' - 'master-new' @@ -31,11 +31,17 @@ on: default: 'master-dev-c3-new' type: string +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + +run-name: ${{ github.event.pull_request.head.repo.fork && (contains('pull_request', github.event_name) && github.event.action == 'synchronize') && format('{0} - {1}', github.workflow, github.event.action) || format('{0} - {1}', github.workflow, github.event_name) }} + jobs: manage-pr-labels: name: Remove trust-fork-pr label if present runs-on: ubuntu-latest - if: (github.event.pull_request.head.repo.fork && (github.event_name == 'pull_request_target' && github.event.action == 'synchronize')) + if: (github.event.pull_request.head.repo.fork && (contains('pull_request', github.event_name) && github.event.action == 'synchronize')) steps: - name: Check if PR has dev-c3 label id: check-labels @@ -89,7 +95,7 @@ jobs: if: ( (github.event_name == 'workflow_dispatch') || (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) - || (github.event_name == 'pull_request_target' && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) + || (contains('pull_request', github.event_name) && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) ) steps: - uses: actions/checkout@v4 @@ -101,7 +107,7 @@ jobs: uses: ./.github/workflows/wait-for-action # Path to where you place the action if: ( (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) - || (github.event_name == 'pull_request_target' && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) + || (contains('pull_request', github.event_name) && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) ) with: workflow: selfdrive_tests.yaml # The workflow file to monitor diff --git a/release/ci/squash_and_merge_prs.py b/release/ci/squash_and_merge_prs.py index 64fa325512..b26b08d500 100755 --- a/release/ci/squash_and_merge_prs.py +++ b/release/ci/squash_and_merge_prs.py @@ -76,9 +76,36 @@ def validate_pr(pr): if not commits: return False, "no commit data found" + # First check if we have the rollup status status = commits[0].get('commit', {}).get('statusCheckRollup', {}) + + # If status is not SUCCESS, we need to check individual check runs if not status or status.get('state') != 'SUCCESS': - return False, "not all checks have passed" + # Get detailed check runs for this PR + checks_output = subprocess.run( + ['gh', 'pr', 'checks', str(pr_number), '--json', 'name,state'], + capture_output=True, text=True + ) + + try: + checks_data = json.loads(checks_output.stdout) + + # Check if all checks are successful except for our reset-and-squash check + for check in checks_data: + check_name = check.get('name', '') + check_state = check.get('state', '') + + # Skip our own check and any skipped checks + if check_name == 'reset-and-squash' or check_state == 'SKIPPED': + continue + + # If any other check is not successful, the PR is not valid + if check_state != 'SUCCESS': + return False, f"check '{check_name}' has state '{check_state}'" + + except json.JSONDecodeError: + # If we can't parse the JSON, fall back to the original check + return False, "unable to verify check status" # Check for merge conflicts merge_status = subprocess.run(['gh', 'pr', 'view', str(pr_number), '--json', 'mergeable,mergeStateStatus'], @@ -87,9 +114,6 @@ def validate_pr(pr): if not merge_data.get('mergeable'): return False, "merge conflicts detected" - # if (mergeStateStatus := merge_data.get('mergeStateStatus')) == "BEHIND": - # return False, f"branch is `{mergeStateStatus}`" - return True, None From 511e4c59198b08384e46619c94d066ee95204ac4 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 20 Apr 2025 13:34:24 +0200 Subject: [PATCH 038/477] ci: update auto pr review config and split turst fork pr (#842) * Updating auto pr labeler * auto pr review revamp * Leaving event as pull request :) * Check permission * add chore * Bringing back as original --- .github/labeler.yaml | 6 +- .github/workflows/auto_pr_review.yaml | 77 ++++++++++++++----- .../sunnypilot-master-dev-c3-prep.yaml | 55 +------------ 3 files changed, 64 insertions(+), 74 deletions(-) diff --git a/.github/labeler.yaml b/.github/labeler.yaml index 0b1fcac0ec..01b6ae9343 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -1,7 +1,11 @@ -CI / testing: +ci: - changed-files: - any-glob-to-all-files: "{.github/**,**/test_*,Jenkinsfile}" +chore: + - changed-files: + - any-glob-to-all-files: "{.github/**}" + car: - changed-files: - any-glob-to-all-files: '{selfdrive/car/**,opendbc_repo}' diff --git a/.github/workflows/auto_pr_review.yaml b/.github/workflows/auto_pr_review.yaml index 33940b6d4b..b7bc015007 100644 --- a/.github/workflows/auto_pr_review.yaml +++ b/.github/workflows/auto_pr_review.yaml @@ -9,6 +9,7 @@ jobs: permissions: contents: read pull-requests: write + issues: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -28,25 +29,63 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - target: /^(?!master$).*/ - exclude: /commaai:.*/ + target: /^(?!master-new$).*/ + exclude: /sunnypilot:.*/ change-to: ${{ github.base_ref }} already-exists-action: close_this - already-exists-comment: "Your PR should be made against the `master` branch" + already-exists-comment: "Your PR should be made against the `master-new` branch" - # Welcome comment - - name: "First timers PR" - uses: actions/first-interaction@v1 - if: github.event.pull_request.head.repo.full_name != 'sunnypilot/sunnypilot' - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - pr-message: | - - Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following: - * Convert your PR to a draft unless it's ready to review - * Read the [contributing docs](https://github.com/sunnypilot/sunnypilot/blob/master/docs/CONTRIBUTING.md) - * Before marking as "ready for review", ensure: - * the goal is clearly stated in the description - * all the tests are passing - * the change is [something we merge](https://github.com/sunnypilot/sunnypilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged) - * include a route or your device' dongle ID if relevant + update-pr-labels: + name: Update fork's PR Labels + runs-on: ubuntu-latest + if: (github.event.pull_request.head.repo.fork && (contains('pull_request', github.event_name) && github.event.action == 'synchronize')) + env: + PR_LABEL: 'dev-c3' + TRUST_FORK_PR_LABEL: 'trust-fork-pr' + steps: + - name: Check if PR has dev-c3 label + id: check-labels + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.pull_request.number; + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber + }); + + const hasDevC3Label = labels.some(label => label.name === process.env.PR_LABEL); + const hasTrustLabel = labels.some(label => label.name === process.env.TRUST_FORK_PR_LABEL); + + console.log(`PR #${prNumber} has ${process.env.PR_LABEL} label: ${hasDevC3Label}`); + console.log(`PR #${prNumber} has ${process.env.TRUST_FORK_PR_LABEL} label: ${hasTrustLabel}`); + + core.setOutput('has-dev-c3', hasDevC3Label ? 'true' : 'false'); + core.setOutput('has-trust', hasTrustLabel ? 'true' : 'false'); + + - name: Remove trust-fork-pr label if present + if: steps.check-labels.outputs.has-dev-c3 == 'true' && steps.check-labels.outputs.has-trust == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.pull_request.number; + + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + name: process.env.TRUST_FORK_PR_LABEL + }); + + console.log(`Removed '${process.env.TRUST_FORK_PR_LABEL}' label from PR #${prNumber} as it received new commits`); + + // Add a comment to the PR + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `The \`${process.env.TRUST_FORK_PR_LABEL}\` label has been automatically removed because new commits were pushed to this PR. This PR will need to be re-reviewed before the label can be applied again.` + }); diff --git a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml index 7fdcb368ec..2d6924d204 100644 --- a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml +++ b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml @@ -4,7 +4,6 @@ env: DEFAULT_SOURCE_BRANCH: "master-new" DEFAULT_TARGET_BRANCH: "master-dev-c3-new" PR_LABEL: "dev-c3" - TRUST_FORK_PR_LABEL: "trust-fork-pr" LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs' LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git' @@ -35,61 +34,9 @@ concurrency: group: ${{ github.workflow }} cancel-in-progress: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} -run-name: ${{ github.event.pull_request.head.repo.fork && (contains('pull_request', github.event_name) && github.event.action == 'synchronize') && format('{0} - {1}', github.workflow, github.event.action) || format('{0} - {1}', github.workflow, github.event_name) }} +run-name: ${{ contains('pull_request', github.event_name) && github.event.action == 'synchronize' && format('{0} - {1}', github.workflow, github.event.action) || format('{0} - {1}', github.workflow, github.event_name) }} jobs: - manage-pr-labels: - name: Remove trust-fork-pr label if present - runs-on: ubuntu-latest - if: (github.event.pull_request.head.repo.fork && (contains('pull_request', github.event_name) && github.event.action == 'synchronize')) - steps: - - name: Check if PR has dev-c3 label - id: check-labels - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const prNumber = context.payload.pull_request.number; - const { data: labels } = await github.rest.issues.listLabelsOnIssue({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber - }); - - const hasDevC3Label = labels.some(label => label.name === process.env.PR_LABEL); - const hasTrustLabel = labels.some(label => label.name === process.env.TRUST_FORK_PR_LABEL); - - console.log(`PR #${prNumber} has ${process.env.PR_LABEL} label: ${hasDevC3Label}`); - console.log(`PR #${prNumber} has ${process.env.TRUST_FORK_PR_LABEL} label: ${hasTrustLabel}`); - - core.setOutput('has-dev-c3', hasDevC3Label ? 'true' : 'false'); - core.setOutput('has-trust', hasTrustLabel ? 'true' : 'false'); - - - name: Remove trust-fork-pr label if present - if: steps.check-labels.outputs.has-dev-c3 == 'true' && steps.check-labels.outputs.has-trust == 'true' - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const prNumber = context.payload.pull_request.number; - - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - name: process.env.TRUST_FORK_PR_LABEL - }); - - console.log(`Removed '${process.env.TRUST_FORK_PR_LABEL}' label from PR #${prNumber} as it received new commits`); - - // Add a comment to the PR - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: `The \`${process.env.TRUST_FORK_PR_LABEL}\` label has been automatically removed because new commits were pushed to this PR. This PR will need to be re-reviewed before the label can be applied again.` - }); - reset-and-squash: runs-on: ubuntu-latest if: ( From d0c209575e5419674de94e364077d508b53b0f7c Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 20 Apr 2025 14:06:24 +0200 Subject: [PATCH 039/477] ci: bugfix flipped contains check for pull requests --- .github/workflows/auto_pr_review.yaml | 2 +- .github/workflows/sunnypilot-build-prebuilt.yaml | 6 +++--- .github/workflows/sunnypilot-master-dev-c3-prep.yaml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/auto_pr_review.yaml b/.github/workflows/auto_pr_review.yaml index b7bc015007..d3cfa60d92 100644 --- a/.github/workflows/auto_pr_review.yaml +++ b/.github/workflows/auto_pr_review.yaml @@ -38,7 +38,7 @@ jobs: update-pr-labels: name: Update fork's PR Labels runs-on: ubuntu-latest - if: (github.event.pull_request.head.repo.fork && (contains('pull_request', github.event_name) && github.event.action == 'synchronize')) + if: (github.event.pull_request.head.repo.fork && (contains(github.event_name, 'pull_request') && github.event.action == 'synchronize')) env: PR_LABEL: 'dev-c3' TRUST_FORK_PR_LABEL: 'trust-fork-pr' diff --git a/.github/workflows/sunnypilot-build-prebuilt.yaml b/.github/workflows/sunnypilot-build-prebuilt.yaml index 241228598d..50c47c510e 100644 --- a/.github/workflows/sunnypilot-build-prebuilt.yaml +++ b/.github/workflows/sunnypilot-build-prebuilt.yaml @@ -34,7 +34,7 @@ on: jobs: validate_tests: runs-on: ubuntu-24.04 - if: ((github.event_name == 'workflow_dispatch' && inputs.wait_for_tests) || contains('pull_request', github.event_name) && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) + if: ((github.event_name == 'workflow_dispatch' && inputs.wait_for_tests) || contains(github.event_name, 'pull_request') && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) steps: - uses: actions/checkout@v4 - name: Wait for Tests @@ -210,7 +210,7 @@ jobs: concurrency: group: publish-${{ github.head_ref || github.ref_name }} cancel-in-progress: true - if: ${{ !contains('pull_request', github.event_name) || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt') }} + if: ${{ !contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt') }} needs: build runs-on: ubuntu-24.04 environment: ${{ (contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) || contains(github.event.pull_request.labels.*.name, 'prebuilt')) && 'auto-deploy' || 'feature-branch' }} @@ -289,7 +289,7 @@ jobs: manage-pr-labels: name: Remove prebuilt label runs-on: ubuntu-latest - if: (always() && contains('pull_request', github.event_name) && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) + if: (always() && contains(github.event_name, 'pull_request') && (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) env: LABEL: prebuilt steps: diff --git a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml index 2d6924d204..1385a32aa5 100644 --- a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml +++ b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml @@ -34,7 +34,7 @@ concurrency: group: ${{ github.workflow }} cancel-in-progress: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} -run-name: ${{ contains('pull_request', github.event_name) && github.event.action == 'synchronize' && format('{0} - {1}', github.workflow, github.event.action) || format('{0} - {1}', github.workflow, github.event_name) }} +run-name: ${{ contains(github.event_name, 'pull_request') && github.event.action == 'synchronize' && format('{0} - {1}', github.workflow, github.event.action) || format('{0} - {1}', github.workflow, github.event_name) }} jobs: reset-and-squash: @@ -42,7 +42,7 @@ jobs: if: ( (github.event_name == 'workflow_dispatch') || (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) - || (contains('pull_request', github.event_name) && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) + || (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) ) steps: - uses: actions/checkout@v4 @@ -54,7 +54,7 @@ jobs: uses: ./.github/workflows/wait-for-action # Path to where you place the action if: ( (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) - || (contains('pull_request', github.event_name) && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) + || (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == 'dev-c3' || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, 'dev-c3')))) ) with: workflow: selfdrive_tests.yaml # The workflow file to monitor From 9a1e420fa781ca059337b8b665e38ff53027e4e8 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 20 Apr 2025 14:19:31 +0200 Subject: [PATCH 040/477] ci: bugfix cancelling only when push to master or workflow dispatch. Otherwise don't let it cancel as it seems that when running with pull_request_target we are with ref pointing to target and thus the cancel condition was being met --- .github/workflows/sunnypilot-master-dev-c3-prep.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml index 1385a32aa5..031be6a50d 100644 --- a/.github/workflows/sunnypilot-master-dev-c3-prep.yaml +++ b/.github/workflows/sunnypilot-master-dev-c3-prep.yaml @@ -13,7 +13,7 @@ on: - master - master-new pull_request_target: - types: [ synchronize, labeled ] + types: [ labeled ] branches: - 'master' - 'master-new' @@ -29,12 +29,15 @@ on: required: true default: 'master-dev-c3-new' type: string + cancel_in_progress: + description: 'Cancel any in-progress runs of this workflow' + required: false + default: true + type: boolean concurrency: group: ${{ github.workflow }} - cancel-in-progress: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - -run-name: ${{ contains(github.event_name, 'pull_request') && github.event.action == 'synchronize' && format('{0} - {1}', github.workflow, github.event.action) || format('{0} - {1}', github.workflow, github.event_name) }} + cancel-in-progress: ${{ inputs.cancel_in_progress || github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: reset-and-squash: From d56baa0fbb8f99f3ee6dc6ad3f9fbc5aee2b01e2 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sun, 20 Apr 2025 15:03:49 +0100 Subject: [PATCH 041/477] ui(raylib): constant spinner rotation speed (#35037) --- system/ui/spinner.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index edf1fe5ca9..adfb604de9 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -9,7 +9,7 @@ from openpilot.system.ui.lib.application import gui_app # Constants PROGRESS_BAR_WIDTH = 1000 PROGRESS_BAR_HEIGHT = 20 -ROTATION_TIME_SECONDS = 1.0 # Time for one full circle +DEGREES_PER_SECOND = 360.0 # one full rotation per second MARGIN = 200 TEXTURE_SIZE = 360 FONT_SIZE = 80 @@ -43,10 +43,8 @@ class Spinner: spinner_origin = rl.Vector2(TEXTURE_SIZE / 2.0, TEXTURE_SIZE / 2.0) comma_position = rl.Vector2(center.x - TEXTURE_SIZE / 2.0, center.y - TEXTURE_SIZE / 2.0) - fps = rl.get_fps() - if fps > 0: - degrees_per_frame = 360.0 / (ROTATION_TIME_SECONDS * fps) - self._rotation = (self._rotation + degrees_per_frame) % 360.0 + delta_time = rl.get_frame_time() + self._rotation = (self._rotation + DEGREES_PER_SECOND * delta_time) % 360.0 # Draw rotating spinner and static comma logo rl.draw_texture_pro(self._spinner_texture, rl.Rectangle(0, 0, TEXTURE_SIZE, TEXTURE_SIZE), From b64ceb41ed903703138db323936f09835032fd2a Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 20 Apr 2025 21:18:11 +0200 Subject: [PATCH 042/477] ci: improve conditions for publish and notify steps in workflow (#844) --- .github/workflows/sunnypilot-build-prebuilt.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sunnypilot-build-prebuilt.yaml b/.github/workflows/sunnypilot-build-prebuilt.yaml index 50c47c510e..156e10515b 100644 --- a/.github/workflows/sunnypilot-build-prebuilt.yaml +++ b/.github/workflows/sunnypilot-build-prebuilt.yaml @@ -206,12 +206,13 @@ jobs: run: | PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable + publish: concurrency: group: publish-${{ github.head_ref || github.ref_name }} cancel-in-progress: true - if: ${{ !contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt') }} - needs: build + if: ${{ (always() && !failure() && !cancelled()) && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }} + needs: [ build ] runs-on: ubuntu-24.04 environment: ${{ (contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) || contains(github.event.pull_request.labels.*.name, 'prebuilt')) && 'auto-deploy' || 'feature-branch' }} steps: @@ -257,7 +258,7 @@ jobs: notify: needs: [ build, publish ] runs-on: ubuntu-24.04 - if: success() + if: ${{ (always() && !failure() && !cancelled()) }} steps: - uses: actions/checkout@v4 - name: Setup Alpine Linux environment From ed555cb948ffcfd8a233938a6caba7fa93d30d2d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 12:48:11 -0700 Subject: [PATCH 043/477] [bot] Update translations (#35040) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_de.ts | 590 +++++++++++++-------------- 1 file changed, 295 insertions(+), 295 deletions(-) diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index f279a8d9f1..986c0fa54f 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -3,14 +3,14 @@ AbstractAlert - + Close Schließen - - + + Snooze Update Update pausieren - + Reboot and Update Aktualisieren und neu starten @@ -66,26 +66,26 @@ Prevent large data uploads when on a metered connection Hochladen großer Dateien über getaktete Verbindungen unterbinden - - Hidden Network - Verborgenes Netzwerk - - - CONNECT - VERBINDEN - - - Enter SSID - SSID eingeben - - - Enter password - Passwort eingeben - - - for "%1" - für "%1" - + + Hidden Network + Verborgenes Netzwerk + + + CONNECT + VERBINDEN + + + Enter SSID + SSID eingeben + + + Enter password + Passwort eingeben + + + for "%1" + für "%1" + ConfirmationDialog @@ -115,34 +115,34 @@ DeveloperPanel - - Joystick Debug Mode - Joystick Debug-Modus - - - Longitudinal Maneuver Mode - Längsmanöver-Modus - - - openpilot Longitudinal Control (Alpha) - openpilot Längsregelung (Alpha) - - - WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - WARNUNG: Die openpilot Längsregelung befindet sich für dieses Fahrzeug im Alpha-Stadium und deaktiviert das automatische Notbremsen (AEB). - - - On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. - Bei diesem Fahrzeug verwendet openpilot standardmäßig den eingebauten Tempomaten anstelle der openpilot Längsregelung. Aktiviere diese Option, um auf die openpilot Längsregelung umzuschalten. Es wird empfohlen, den experimentellen Modus zu aktivieren, wenn die openpilot Längsregelung (Alpha) aktiviert wird. - - - Enable ADB - ADB aktivieren - - - ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. - ADB (Android Debug Bridge) ermöglicht die Verbindung zu deinem Gerät über USB oder Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-comma für weitere Informationen. - + + Joystick Debug Mode + Joystick Debug-Modus + + + Longitudinal Maneuver Mode + Längsmanöver-Modus + + + openpilot Longitudinal Control (Alpha) + openpilot Längsregelung (Alpha) + + + WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). + WARNUNG: Die openpilot Längsregelung befindet sich für dieses Fahrzeug im Alpha-Stadium und deaktiviert das automatische Notbremsen (AEB). + + + On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. + Bei diesem Fahrzeug verwendet openpilot standardmäßig den eingebauten Tempomaten anstelle der openpilot Längsregelung. Aktiviere diese Option, um auf die openpilot Längsregelung umzuschalten. Es wird empfohlen, den experimentellen Modus zu aktivieren, wenn die openpilot Längsregelung (Alpha) aktiviert wird. + + + Enable ADB + ADB aktivieren + + + ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info. + ADB (Android Debug Bridge) ermöglicht die Verbindung zu deinem Gerät über USB oder Netzwerk. Siehe https://docs.comma.ai/how-to/connect-to-comma für weitere Informationen. + DevicePanel @@ -278,14 +278,14 @@ Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. Koppele dein Gerät mit Comma Connect (connect.comma.ai) und sichere dir dein Comma Prime Angebot. - - Pair Device - Gerät koppeln - - - PAIR - KOPPELN - + + Pair Device + Gerät koppeln + + + PAIR + KOPPELN + DriverViewWindow @@ -307,41 +307,41 @@ FirehosePanel - - 🔥 Firehose Mode 🔥 - 🔥 Firehose-Modus 🔥 - - - openpilot learns to drive by watching humans, like you, drive. + + 🔥 Firehose Mode 🔥 + 🔥 Firehose-Modus 🔥 + + + openpilot learns to drive by watching humans, like you, drive. Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode. - openpilot lernt das Fahren, indem es Menschen wie dir beim Fahren zuschaut. + openpilot lernt das Fahren, indem es Menschen wie dir beim Fahren zuschaut. Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximieren, um die Fahrmodelle von openpilot zu verbessern. Mehr Daten bedeuten größere Modelle, was zu einem besseren Experimentellen Modus führt. - - - Firehose Mode: ACTIVE - Firehose-Modus: AKTIV - - - ACTIVE - AKTIV - - - For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - Für maximale Effektivität bring dein Gerät jede Woche nach drinnen und verbinde es mit einem guten USB-C-Adapter und WLAN.<br><br>Der Firehose-Modus funktioniert auch während der Fahrt, wenn das Gerät mit einem Hotspot oder einer ungedrosselten SIM-Karte verbunden ist.<br><br><br><b>Häufig gestellte Fragen</b><br><br><i>Spielt es eine Rolle, wie oder wo ich fahre?</i> Nein, fahre einfach wie gewohnt.<br><br><i>Werden im Firehose-Modus alle meine Segmente hochgeladen?</i> Nein, wir wählen selektiv nur einen Teil deiner Segmente aus.<br><br><i>Welcher USB-C-Adapter ist gut?</i> Jedes Schnellladegerät für Handy oder Laptop sollte ausreichen.<br><br><i>Spielt es eine Rolle, welche Software ich nutze?</i> Ja, nur das offizielle Upstream‑openpilot (und bestimmte Forks) kann für das Training verwendet werden. - - - <b>%n segment(s)</b> of your driving is in the training dataset so far. - - <b>%n Segment</b> deiner Fahrten ist bisher im Trainingsdatensatz. - <b>%n Segmente</b> deiner Fahrten sind bisher im Trainingsdatensatz. - - - - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network - <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INAKTIV</span>: Verbinde dich mit einem ungedrosselten Netzwerk - + + + Firehose Mode: ACTIVE + Firehose-Modus: AKTIV + + + ACTIVE + AKTIV + + + For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. + Für maximale Effektivität bring dein Gerät jede Woche nach drinnen und verbinde es mit einem guten USB-C-Adapter und WLAN.<br><br>Der Firehose-Modus funktioniert auch während der Fahrt, wenn das Gerät mit einem Hotspot oder einer ungedrosselten SIM-Karte verbunden ist.<br><br><br><b>Häufig gestellte Fragen</b><br><br><i>Spielt es eine Rolle, wie oder wo ich fahre?</i> Nein, fahre einfach wie gewohnt.<br><br><i>Werden im Firehose-Modus alle meine Segmente hochgeladen?</i> Nein, wir wählen selektiv nur einen Teil deiner Segmente aus.<br><br><i>Welcher USB-C-Adapter ist gut?</i> Jedes Schnellladegerät für Handy oder Laptop sollte ausreichen.<br><br><i>Spielt es eine Rolle, welche Software ich nutze?</i> Ja, nur das offizielle Upstream‑openpilot (und bestimmte Forks) kann für das Training verwendet werden. + + + <b>%n segment(s)</b> of your driving is in the training dataset so far. + + <b>%n Segment</b> deiner Fahrten ist bisher im Trainingsdatensatz. + <b>%n Segmente</b> deiner Fahrten sind bisher im Trainingsdatensatz. + + + + <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to an unmetered network + <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INAKTIV</span>: Verbinde dich mit einem ungedrosselten Netzwerk + HudRenderer @@ -419,44 +419,44 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Connect to internet to check for updates. openpilot won't automatically start until it connects to internet to check for updates. Verbinde dich mit dem Internet, um nach Updates zu suchen. openpilot startet nicht automatisch, bis eine Internetverbindung besteht und nach Updates gesucht wurde. - - Unable to download updates + + Unable to download updates %1 - Updates konnten nicht heruntergeladen werden + Updates konnten nicht heruntergeladen werden %1 - - - Taking camera snapshots. System won't start until finished. - Kamera-Snapshots werden aufgenommen. Das System startet erst, wenn dies abgeschlossen ist. - - - An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. - Ein Update für das Betriebssystem deines Geräts wird im Hintergrund heruntergeladen. Du wirst aufgefordert, das Update zu installieren, sobald es bereit ist. - - - Device failed to register. It will not connect to or upload to comma.ai servers, and receives no support from comma.ai. If this is an official device, visit https://comma.ai/support. - Gerät konnte nicht registriert werden. Es wird keine Verbindung zu den comma.ai-Servern herstellen oder Daten hochladen und erhält keinen Support von comma.ai. Wenn dies ein offizielles Gerät ist, besuche https://comma.ai/support. - - - NVMe drive not mounted. - NVMe-Laufwerk nicht gemounted. - - - Unsupported NVMe drive detected. Device may draw significantly more power and overheat due to the unsupported NVMe. - Nicht unterstütztes NVMe-Laufwerk erkannt. Das Gerät kann dadurch deutlich mehr Strom verbrauchen und überhitzen. - - - openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. - openpilot konnte dein Auto nicht identifizieren. Dein Auto wird entweder nicht unterstützt oder die Steuergeräte (ECUs) werden nicht erkannt. Bitte reiche einen Pull Request ein, um die Firmware-Versionen für das richtige Fahrzeug hinzuzufügen. Hilfe findest du auf discord.comma.ai. - - - openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. - openpilot hat eine Änderung der Montageposition des Geräts erkannt. Stelle sicher, dass das Gerät vollständig in der Halterung sitzt und die Halterung fest an der Windschutzscheibe befestigt ist. - - - Device temperature too high. System cooling down before starting. Current internal component temperature: %1 - Gerätetemperatur zu hoch. Das System kühlt ab, bevor es startet. Aktuelle interne Komponententemperatur: %1 - + + + Taking camera snapshots. System won't start until finished. + Kamera-Snapshots werden aufgenommen. Das System startet erst, wenn dies abgeschlossen ist. + + + An update to your device's operating system is downloading in the background. You will be prompted to update when it's ready to install. + Ein Update für das Betriebssystem deines Geräts wird im Hintergrund heruntergeladen. Du wirst aufgefordert, das Update zu installieren, sobald es bereit ist. + + + Device failed to register. It will not connect to or upload to comma.ai servers, and receives no support from comma.ai. If this is an official device, visit https://comma.ai/support. + Gerät konnte nicht registriert werden. Es wird keine Verbindung zu den comma.ai-Servern herstellen oder Daten hochladen und erhält keinen Support von comma.ai. Wenn dies ein offizielles Gerät ist, besuche https://comma.ai/support. + + + NVMe drive not mounted. + NVMe-Laufwerk nicht gemounted. + + + Unsupported NVMe drive detected. Device may draw significantly more power and overheat due to the unsupported NVMe. + Nicht unterstütztes NVMe-Laufwerk erkannt. Das Gerät kann dadurch deutlich mehr Strom verbrauchen und überhitzen. + + + openpilot was unable to identify your car. Your car is either unsupported or its ECUs are not recognized. Please submit a pull request to add the firmware versions to the proper vehicle. Need help? Join discord.comma.ai. + openpilot konnte dein Auto nicht identifizieren. Dein Auto wird entweder nicht unterstützt oder die Steuergeräte (ECUs) werden nicht erkannt. Bitte reiche einen Pull Request ein, um die Firmware-Versionen für das richtige Fahrzeug hinzuzufügen. Hilfe findest du auf discord.comma.ai. + + + openpilot detected a change in the device's mounting position. Ensure the device is fully seated in the mount and the mount is firmly secured to the windshield. + openpilot hat eine Änderung der Montageposition des Geräts erkannt. Stelle sicher, dass das Gerät vollständig in der Halterung sitzt und die Halterung fest an der Windschutzscheibe befestigt ist. + + + Device temperature too high. System cooling down before starting. Current internal component temperature: %1 + Gerätetemperatur zu hoch. Das System kühlt ab, bevor es startet. Aktuelle interne Komponententemperatur: %1 + OffroadHome @@ -475,26 +475,26 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere OnroadAlerts - - openpilot Unavailable - openpilot nicht verfügbar - - - TAKE CONTROL IMMEDIATELY - ÜBERNIMM SOFORT DIE KONTROLLE - - - Reboot Device - Gerät neu starten - - - Waiting to start - Warten auf Start - - - System Unresponsive - System reagiert nicht - + + openpilot Unavailable + openpilot nicht verfügbar + + + TAKE CONTROL IMMEDIATELY + ÜBERNIMM SOFORT DIE KONTROLLE + + + Reboot Device + Gerät neu starten + + + Waiting to start + Warten auf Start + + + System Unresponsive + System reagiert nicht + PairingPopup @@ -514,10 +514,10 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Bookmark connect.comma.ai to your home screen to use it like an app Füge connect.comma.ai als Lesezeichen auf deinem Homescreen hinzu um es wie eine App zu verwenden - - Please connect to Wi-Fi to complete initial pairing - Bitte verbinde dich mit WLAN, um die Koppelung abzuschließen. - + + Please connect to Wi-Fi to complete initial pairing + Bitte verbinde dich mit WLAN, um die Koppelung abzuschließen. + ParamControl @@ -548,18 +548,18 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Remote access Fernzugriff - - 24/7 LTE connectivity - 24/7 LTE-Verbindung - - - 1 year of drive storage - Fahrdaten-Speicherung für 1 Jahr - - - Remote snapshots - Remote-Snapshots - + + 24/7 LTE connectivity + 24/7 LTE-Verbindung + + + 1 year of drive storage + Fahrdaten-Speicherung für 1 Jahr + + + Remote snapshots + Remote-Snapshots + PrimeUserWidget @@ -607,10 +607,10 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere vor %n Tagen - - now - jetzt - + + now + jetzt + Reset @@ -638,20 +638,20 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere Confirm Bestätigen - - Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. - Datenpartition konnte nicht gemounted werden. Die Partition ist möglicherweise beschädigt. Drücke Bestätigen, um das Gerät zu löschen und zurückzusetzen. - - - Resetting device... + + Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. + Datenpartition konnte nicht gemounted werden. Die Partition ist möglicherweise beschädigt. Drücke Bestätigen, um das Gerät zu löschen und zurückzusetzen. + + + Resetting device... This may take up to a minute. - Gerät wird zurückgesetzt... + Gerät wird zurückgesetzt... Dies kann bis zu einer Minute dauern. - - - System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. - System-Reset ausgelöst. Drücke Bestätigen, um alle Inhalte und Einstellungen zu löschen. Drücke Abbrechen, um den Startvorgang fortzusetzen. - + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + System-Reset ausgelöst. Drücke Bestätigen, um alle Inhalte und Einstellungen zu löschen. Drücke Abbrechen, um den Startvorgang fortzusetzen. + SettingsWindow @@ -675,14 +675,14 @@ Dies kann bis zu einer Minute dauern. Software Software - - Developer - Entwickler - - - Firehose - Firehose - + + Developer + Entwickler + + + Firehose + Firehose + Setup @@ -754,30 +754,30 @@ Dies kann bis zu einer Minute dauern. Start over Von neuem beginnen - - No custom software found at this URL. - Keine benutzerdefinierte Software unter dieser URL gefunden. - - - Something went wrong. Reboot the device. - Etwas ist schiefgelaufen. Starte das Gerät neu. - + + No custom software found at this URL. + Keine benutzerdefinierte Software unter dieser URL gefunden. + + + Something went wrong. Reboot the device. + Etwas ist schiefgelaufen. Starte das Gerät neu. + Select a language Sprache wählen - - Choose Software to Install - Wähle die zu installierende Software - - - openpilot - openpilot - - - Custom Software - Benutzerdefinierte Software - + + Choose Software to Install + Wähle die zu installierende Software + + + openpilot + openpilot + + + Custom Software + Benutzerdefinierte Software + SetupWidget @@ -925,26 +925,26 @@ Dies kann bis zu einer Minute dauern. Uninstall Deinstallieren - - failed to check for update - Update-Prüfung fehlgeschlagen - - - up to date, last checked %1 - Auf dem neuesten Stand, zuletzt geprüft am %1 - - - DOWNLOAD - HERUNTERLADEN - - - update available - Update verfügbar - - - never - nie - + + failed to check for update + Update-Prüfung fehlgeschlagen + + + up to date, last checked %1 + Auf dem neuesten Stand, zuletzt geprüft am %1 + + + DOWNLOAD + HERUNTERLADEN + + + update available + Update verfügbar + + + never + nie + SshControl @@ -1002,14 +1002,14 @@ Dies kann bis zu einer Minute dauern. Agree Zustimmen - - Welcome to openpilot - Willkommen bei openpilot - - - You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - Du musst die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden. Lies die aktuellen Bedingungen unter <span style='color: #465BEA;'>https://comma.ai/terms</span>, bevor du fortfährst. - + + Welcome to openpilot + Willkommen bei openpilot + + + You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. + Du musst die Nutzungsbedingungen akzeptieren, um openpilot zu verwenden. Lies die aktuellen Bedingungen unter <span style='color: #465BEA;'>https://comma.ai/terms</span>, bevor du fortfährst. + TogglesPanel @@ -1073,54 +1073,54 @@ Dies kann bis zu einer Minute dauern. Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. Der experimentelle Modus ist momentan für dieses Auto nicht verfügbar da es den eingebauten adaptiven Tempomaten des Autos benutzt. - - Aggressive - Aggressiv - - - Standard - Standard - - - Relaxed - Entspannt - - - Driving Personality - Fahrstil - - - End-to-End Longitudinal Control - Ende-zu-Ende Längsregelung - - - openpilot longitudinal control may come in a future update. - Die openpilot Längsregelung könnte in einem zukünftigen Update verfügbar sein. - - - An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - Eine Alpha-Version der openpilot Längsregelung kann zusammen mit dem Experimentellen Modus auf non-stable Branches getestet werden. - - - Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. - Aktiviere den Schalter für openpilot Längsregelung (Alpha), um den Experimentellen Modus zu erlauben. - - - Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. - Standard wird empfohlen. Im aggressiven Modus folgt openpilot vorausfahrenden Fahrzeugen enger und ist beim Gasgeben und Bremsen aggressiver. Im entspannten Modus hält openpilot mehr Abstand zu vorausfahrenden Fahrzeugen. Bei unterstützten Fahrzeugen kannst du mit der Abstandstaste am Lenkrad zwischen diesen Fahrstilen wechseln. - - - The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. - Die Fahrvisualisierung wechselt bei niedrigen Geschwindigkeiten auf die nach vorne gerichtete Weitwinkelkamera, um Kurven besser darzustellen. Das Logo des Experimentellen Modus wird außerdem oben rechts angezeigt. - - - Always-On Driver Monitoring - Dauerhaft aktive Fahrerüberwachung - - - Enable driver monitoring even when openpilot is not engaged. - Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist. - + + Aggressive + Aggressiv + + + Standard + Standard + + + Relaxed + Entspannt + + + Driving Personality + Fahrstil + + + End-to-End Longitudinal Control + Ende-zu-Ende Längsregelung + + + openpilot longitudinal control may come in a future update. + Die openpilot Längsregelung könnte in einem zukünftigen Update verfügbar sein. + + + An alpha version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. + Eine Alpha-Version der openpilot Längsregelung kann zusammen mit dem Experimentellen Modus auf non-stable Branches getestet werden. + + + Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. + Aktiviere den Schalter für openpilot Längsregelung (Alpha), um den Experimentellen Modus zu erlauben. + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with your steering wheel distance button. + Standard wird empfohlen. Im aggressiven Modus folgt openpilot vorausfahrenden Fahrzeugen enger und ist beim Gasgeben und Bremsen aggressiver. Im entspannten Modus hält openpilot mehr Abstand zu vorausfahrenden Fahrzeugen. Bei unterstützten Fahrzeugen kannst du mit der Abstandstaste am Lenkrad zwischen diesen Fahrstilen wechseln. + + + The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. + Die Fahrvisualisierung wechselt bei niedrigen Geschwindigkeiten auf die nach vorne gerichtete Weitwinkelkamera, um Kurven besser darzustellen. Das Logo des Experimentellen Modus wird außerdem oben rechts angezeigt. + + + Always-On Driver Monitoring + Dauerhaft aktive Fahrerüberwachung + + + Enable driver monitoring even when openpilot is not engaged. + Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist. + Updater @@ -1159,18 +1159,18 @@ Dies kann bis zu einer Minute dauern. WiFiPromptWidget - - Open - Öffnen - - - Maximize your training data uploads to improve openpilot's driving models. - Maximiere deine Trainingsdaten-Uploads, um die Fahrmodelle von openpilot zu verbessern. - - - <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> - <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose-Modus <span style='font-family: Noto Color Emoji;'>🔥</span> - + + Open + Öffnen + + + Maximize your training data uploads to improve openpilot's driving models. + Maximiere deine Trainingsdaten-Uploads, um die Fahrmodelle von openpilot zu verbessern. + + + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span> + <span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose-Modus <span style='font-family: Noto Color Emoji;'>🔥</span> + WifiUI From 34514ef1761037127cc009e5e95b7478b5fc1ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 21 Apr 2025 14:11:57 -0700 Subject: [PATCH 044/477] Long planner: make work with training (#35043) * revert useless * update ref --- selfdrive/controls/lib/longitudinal_planner.py | 6 +++--- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 104c7c9d49..4219e80f33 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -52,6 +52,7 @@ class LongitudinalPlanner: def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL): self.CP = CP self.mpc = LongitudinalMpc(dt=dt) + self.mpc.mode = 'acc' self.fcw = False self.dt = dt self.allow_throttle = True @@ -89,7 +90,6 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mpc.mode = 'acc' self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: @@ -97,7 +97,7 @@ class LongitudinalPlanner: else: accel_coast = ACCEL_MAX - v_ego = sm['modelV2'].velocity.x[0] + v_ego = sm['carState'].vEgo v_cruise_kph = min(sm['carState'].vCruise, V_CRUISE_MAX) v_cruise = v_cruise_kph * CV.KPH_TO_MS v_cruise_initialized = sm['carState'].vCruise != V_CRUISE_UNSET @@ -129,7 +129,7 @@ class LongitudinalPlanner: self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego)) # Compute model v_ego error self.v_model_error = get_speed_error(sm['modelV2'], v_ego) - x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], 0) + x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error) # Don't clip at low speeds since throttle_prob doesn't account for creep self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 5f89d7802d..4b3ef33407 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -0800e73b5d9c801f161b9559557c0559b0682fec \ No newline at end of file +fcc771b9ceb487a61035885acbd84e592fb316bf \ No newline at end of file From 33a7d853f00d7f6f9d1ffc13160816b6106aec72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 21 Apr 2025 15:10:41 -0700 Subject: [PATCH 045/477] Modeld: small refactor (#35044) refactor --- selfdrive/controls/lib/drive_helpers.py | 3 +++ selfdrive/modeld/modeld.py | 8 +------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 9958755577..038061b7e6 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -19,6 +19,9 @@ def clamp(val, min_val, max_val): clamped_val = float(np.clip(val, min_val, max_val)) return clamped_val, clamped_val != val +def smooth_value(val, prev_val, tau): + alpha = 1 - np.exp(-DT_MDL / tau) if tau > 0 else 1 + return alpha * val + (1 - alpha) * prev_val def clip_curvature(v_ego, prev_curvature, new_curvature, roll): # This function respects ISO lateral jerk and acceleration limits + a max curvature diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 0ad629f519..1060b36d28 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -26,15 +26,13 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext - - PROCESS_NAME = "selfdrive.modeld.modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -46,10 +44,6 @@ POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.p LAT_SMOOTH_SECONDS = 0.3 LONG_SMOOTH_SECONDS = 0.3 -def smooth_value(val, prev_val, tau): - alpha = 1 - np.exp(-DT_MDL / tau) if tau > 0 else 1 - return alpha * val + (1 - alpha) * prev_val - def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, lat_action_t: float, long_action_t: float,) -> log.ModelDataV2.Action: From a029d13398c0408b4ebcf7fa916474f5e3d79129 Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Mon, 21 Apr 2025 15:44:30 -0700 Subject: [PATCH 046/477] update smooth --- selfdrive/controls/lib/drive_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 038061b7e6..3f99f1bbbb 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -19,8 +19,8 @@ def clamp(val, min_val, max_val): clamped_val = float(np.clip(val, min_val, max_val)) return clamped_val, clamped_val != val -def smooth_value(val, prev_val, tau): - alpha = 1 - np.exp(-DT_MDL / tau) if tau > 0 else 1 +def smooth_value(val, prev_val, tau, dt=DT_MDL): + alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1 return alpha * val + (1 - alpha) * prev_val def clip_curvature(v_ego, prev_curvature, new_curvature, roll): From a9b9e0bb54dda1613e8347efe146a78d6ce6bc71 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 21 Apr 2025 16:13:55 -0700 Subject: [PATCH 047/477] raylib: init updater (#35045) * raylib: init updater * cleanup --- system/ui/updater.py | 197 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100755 system/ui/updater.py diff --git a/system/ui/updater.py b/system/ui/updater.py new file mode 100755 index 0000000000..eb9d766a16 --- /dev/null +++ b/system/ui/updater.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +import sys +import subprocess +import threading +import pyray as rl +from enum import IntEnum + +from openpilot.system.hardware import HARDWARE +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.button import gui_button, ButtonStyle +from openpilot.system.ui.lib.label import gui_text_box, gui_label + +# Constants +MARGIN = 50 +BUTTON_HEIGHT = 160 +BUTTON_WIDTH = 400 +PROGRESS_BAR_HEIGHT = 72 +TITLE_FONT_SIZE = 80 +BODY_FONT_SIZE = 65 +BACKGROUND_COLOR = rl.BLACK +PROGRESS_BG_COLOR = rl.Color(41, 41, 41, 255) +PROGRESS_COLOR = rl.Color(54, 77, 239, 255) + + +class Screen(IntEnum): + PROMPT = 0 + WIFI = 1 + PROGRESS = 2 + + +class Updater: + def __init__(self, updater_path, manifest_path): + self.updater = updater_path + self.manifest = manifest_path + self.current_screen = Screen.PROMPT + + self.progress_value = 0 + self.progress_text = "Loading..." + self.show_reboot_button = False + self.process = None + self.update_thread = None + + def install_update(self): + self.current_screen = Screen.PROGRESS + self.progress_value = 0 + self.progress_text = "Downloading..." + self.show_reboot_button = False + + # Start the update process in a separate thread + self.update_thread = threading.Thread(target=self._run_update_process) + self.update_thread.daemon = True + self.update_thread.start() + + def _run_update_process(self): + # TODO: just import it and run in a thread without a subprocess + cmd = [self.updater, "--swap", self.manifest] + self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True, bufsize=1, universal_newlines=True) + + for line in self.process.stdout: + parts = line.strip().split(":") + if len(parts) == 2: + self.progress_text = parts[0] + try: + self.progress_value = int(float(parts[1])) + except ValueError: + pass + + exit_code = self.process.wait() + if exit_code == 0: + HARDWARE.reboot() + else: + self.progress_text = "Update failed" + self.show_reboot_button = True + + def render_prompt_screen(self): + # Title + title_rect = rl.Rectangle(MARGIN + 50, 250, gui_app.width - MARGIN * 2 - 100, TITLE_FONT_SIZE) + gui_label(title_rect, "Update Required", TITLE_FONT_SIZE, font_weight=FontWeight.BOLD) + + # Description + desc_text = "An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. \ + The download size is approximately 1GB." + desc_rect = rl.Rectangle(MARGIN + 50, 250 + TITLE_FONT_SIZE + 75, gui_app.width - MARGIN * 2 - 100, BODY_FONT_SIZE * 3) + gui_text_box(desc_rect, desc_text, BODY_FONT_SIZE) + + # Buttons at the bottom + button_y = gui_app.height - MARGIN - BUTTON_HEIGHT + button_width = (gui_app.width - MARGIN * 3) // 2 + + # WiFi button + wifi_button_rect = rl.Rectangle(MARGIN, button_y, button_width, BUTTON_HEIGHT) + if gui_button(wifi_button_rect, "Connect to Wi-Fi"): + self.current_screen = Screen.WIFI + return # Return to avoid processing other buttons after screen change + + # Install button + install_button_rect = rl.Rectangle(MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT) + if gui_button(install_button_rect, "Install", button_style=ButtonStyle.PRIMARY): + self.install_update() + return # Return to avoid further processing after action + + def render_wifi_screen(self): + # Title and back button + title_rect = rl.Rectangle(MARGIN + 50, MARGIN, gui_app.width - MARGIN * 2 - 100, 60) + gui_label(title_rect, "Wi-Fi Networks", 60, font_weight=FontWeight.BOLD) + + back_button_rect = rl.Rectangle(MARGIN, gui_app.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) + if gui_button(back_button_rect, "Back"): + self.current_screen = Screen.PROMPT + return # Return to avoid processing other interactions after screen change + + # Draw placeholder for WiFi implementation + placeholder_rect = rl.Rectangle( + MARGIN, + title_rect.y + title_rect.height + MARGIN, + gui_app.width - MARGIN * 2, + gui_app.height - title_rect.height - MARGIN * 3 - BUTTON_HEIGHT + ) + + # Draw rounded rectangle background + rl.draw_rectangle_rounded( + placeholder_rect, + 0.1, + 10, + rl.Color(41, 41, 41, 255) + ) + + # Draw placeholder text + placeholder_text = "WiFi Implementation Placeholder" + text_size = rl.measure_text_ex(gui_app.font(), placeholder_text, 80, 1) + text_pos = rl.Vector2( + placeholder_rect.x + (placeholder_rect.width - text_size.x) / 2, + placeholder_rect.y + (placeholder_rect.height - text_size.y) / 2 + ) + rl.draw_text_ex(gui_app.font(), placeholder_text, text_pos, 80, 1, rl.WHITE) + + # Draw instructions + instructions_text = "Real WiFi functionality would be implemented here" + instructions_size = rl.measure_text_ex(gui_app.font(), instructions_text, 40, 1) + instructions_pos = rl.Vector2( + placeholder_rect.x + (placeholder_rect.width - instructions_size.x) / 2, + text_pos.y + text_size.y + 20 + ) + rl.draw_text_ex(gui_app.font(), instructions_text, instructions_pos, 40, 1, rl.GRAY) + + def render_progress_screen(self): + title_rect = rl.Rectangle(MARGIN + 100, 330, gui_app.width - MARGIN * 2 - 200, 100) + gui_label(title_rect, self.progress_text, 90, font_weight=FontWeight.SEMI_BOLD) + + # Progress bar + bar_rect = rl.Rectangle(MARGIN + 100, 330 + 100 + 100, gui_app.width - MARGIN * 2 - 200, PROGRESS_BAR_HEIGHT) + rl.draw_rectangle_rounded(bar_rect, 0.5, 10, PROGRESS_BG_COLOR) + + # Calculate the width of the progress chunk + progress_width = (bar_rect.width * self.progress_value) / 100 + if progress_width > 0: + progress_rect = rl.Rectangle(bar_rect.x, bar_rect.y, progress_width, bar_rect.height) + rl.draw_rectangle_rounded(progress_rect, 0.5, 10, PROGRESS_COLOR) + + # Show reboot button if needed + if self.show_reboot_button: + reboot_rect = rl.Rectangle(MARGIN + 100, gui_app.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) + if gui_button(reboot_rect, "Reboot"): + # Return True to signal main loop to exit before rebooting + HARDWARE.reboot() + return + + def render(self): + if self.current_screen == Screen.PROMPT: + self.render_prompt_screen() + elif self.current_screen == Screen.WIFI: + self.render_wifi_screen() + elif self.current_screen == Screen.PROGRESS: + self.render_progress_screen() + + +def main(): + if len(sys.argv) < 3: + print("Usage: updater.py ") + sys.exit(1) + + updater_path = sys.argv[1] + manifest_path = sys.argv[2] + + try: + gui_app.init_window("System Update") + updater = Updater(updater_path, manifest_path) + for _ in gui_app.render(): + updater.render() + finally: + # Make sure we clean up even if there's an error + gui_app.close() + + +if __name__ == "__main__": + main() From aaaa2d0dd064364d647fb381cc488fe6fe4d44e2 Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Mon, 21 Apr 2025 17:02:36 -0700 Subject: [PATCH 048/477] Revert "OS04C10: use IFE downscaler for road cameras" (#35046) Revert "OS04C10: use IFE downscaler for road cameras (#35023)" This reverts commit 3b60b22ceeb090812e5fa948ac6e56442d681dc4. --- system/camerad/cameras/camera_common.cc | 3 ++ system/camerad/cameras/camera_common.h | 2 +- system/camerad/cameras/camera_qcom2.cc | 10 ++-- system/camerad/cameras/ife.h | 18 ++++---- system/camerad/cameras/spectra.cc | 40 +++++++--------- system/camerad/sensors/os04c10.cc | 11 ----- system/camerad/sensors/os04c10_registers.h | 54 ++++++++-------------- system/camerad/sensors/sensor.h | 2 - 8 files changed, 53 insertions(+), 87 deletions(-) diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index 1f6ad9b4be..f6c2681dea 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -26,6 +26,9 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, SpectraCamera * LOGD("allocated %d CL buffers", frame_buf_count); } + out_img_width = sensor->frame_width; + out_img_height = sensor->hdr_offset > 0 ? (sensor->frame_height - sensor->hdr_offset) / 2 : sensor->frame_height; + // the encoder HW tells us the size it wants after setting it up. // TODO: VENUS_BUFFER_SIZE should give the size, but it's too small. dependent on encoder settings? size_t nv12_size = (out_img_width <= 1344 ? 2900 : 2346)*cam->stride; diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h index c26859cbc4..c0426678b6 100644 --- a/system/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -32,7 +32,7 @@ public: VisionBuf *cur_yuv_buf; VisionBuf *cur_camera_buf; std::unique_ptr camera_bufs_raw; - uint32_t out_img_width, out_img_height; + int out_img_width, out_img_height; CameraBuf() = default; ~CameraBuf(); diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 8c4602bb31..18a42f05d8 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -73,7 +73,7 @@ void CameraState::init(VisionIpcServer *v, cl_device_id device_id, cl_context ct if (!camera.enabled) return; - fl_pix = camera.cc.focal_len / camera.sensor->pixel_size_mm / camera.sensor->out_scale; + fl_pix = camera.cc.focal_len / camera.sensor->pixel_size_mm; set_exposure_rect(); dc_gain_weight = camera.sensor->dc_gain_min_weight; @@ -107,10 +107,10 @@ void CameraState::set_exposure_rect() { float fl_ref = ae_target.second; ae_xywh = (Rect){ - std::max(0, (int)camera.buf.out_img_width / 2 - (int)(fl_pix / fl_ref * xywh_ref.w / 2)), - std::max(0, (int)camera.buf.out_img_height / 2 - (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))), - std::min((int)(fl_pix / fl_ref * xywh_ref.w), (int)camera.buf.out_img_width / 2 + (int)(fl_pix / fl_ref * xywh_ref.w / 2)), - std::min((int)(fl_pix / fl_ref * xywh_ref.h), (int)camera.buf.out_img_height / 2 + (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))) + std::max(0, camera.buf.out_img_width / 2 - (int)(fl_pix / fl_ref * xywh_ref.w / 2)), + std::max(0, camera.buf.out_img_height / 2 - (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))), + std::min((int)(fl_pix / fl_ref * xywh_ref.w), camera.buf.out_img_width / 2 + (int)(fl_pix / fl_ref * xywh_ref.w / 2)), + std::min((int)(fl_pix / fl_ref * xywh_ref.h), camera.buf.out_img_height / 2 + (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))) }; } diff --git a/system/camerad/cameras/ife.h b/system/camerad/cameras/ife.h index fd87d2baa4..49737f2db7 100644 --- a/system/camerad/cameras/ife.h +++ b/system/camerad/cameras/ife.h @@ -105,7 +105,7 @@ int build_update(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std: } -int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std::vector &patches, uint32_t out_width, uint32_t out_height) { +int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std::vector &patches) { uint8_t *start = dst; // start with the every frame config @@ -185,12 +185,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo // output size/scaling dst += write_cont(dst, 0xa3c, { 0x00000003, - ((out_width - 1) << 16) | (s->frame_width - 1), + ((s->frame_width - 1) << 16) | (s->frame_width - 1), 0x30036666, 0x00000000, 0x00000000, s->frame_width - 1, - ((out_height - 1) << 16) | (s->frame_height - 1), + ((s->frame_height - 1) << 16) | (s->frame_height - 1), 0x30036666, 0x00000000, 0x00000000, @@ -198,12 +198,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo }); dst += write_cont(dst, 0xa68, { 0x00000003, - ((out_width / 2 - 1) << 16) | (s->frame_width - 1), + ((s->frame_width/2 - 1) << 16) | (s->frame_width - 1), 0x3006cccc, 0x00000000, 0x00000000, s->frame_width - 1, - ((out_height / 2 - 1) << 16) | (s->frame_height - 1), + ((s->frame_height/2 - 1) << 16) | (s->frame_height - 1), 0x3006cccc, 0x00000000, 0x00000000, @@ -212,12 +212,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo // cropping dst += write_cont(dst, 0xe10, { - out_height - 1, - out_width - 1, + s->frame_height - 1, + s->frame_width - 1, }); dst += write_cont(dst, 0xe30, { - out_height / 2 - 1, - out_width - 1, + s->frame_height/2 - 1, + s->frame_width - 1, }); dst += write_cont(dst, 0xe18, { 0x0ff00000, diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc index 47ae9061f4..4535078771 100644 --- a/system/camerad/cameras/spectra.cc +++ b/system/camerad/cameras/spectra.cc @@ -281,21 +281,18 @@ void SpectraCamera::camera_open(VisionIpcServer *v, cl_device_id device_id, cl_c if (!enabled) return; - buf.out_img_width = sensor->frame_width / sensor->out_scale; - buf.out_img_height = (sensor->hdr_offset > 0 ? (sensor->frame_height - sensor->hdr_offset) / 2 : sensor->frame_height) / sensor->out_scale; - // size is driven by all the HW that handles frames, // the video encoder has certain alignment requirements in this case - stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, buf.out_img_width); - y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, buf.out_img_height); - uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, buf.out_img_height); + stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, sensor->frame_width); + y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, sensor->frame_height); + uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, sensor->frame_height); uv_offset = stride*y_height; yuv_size = uv_offset + stride*uv_height; if (cc.output_type != ISP_RAW_OUTPUT) { uv_offset = ALIGNED_SIZE(uv_offset, 0x1000); yuv_size = uv_offset + ALIGNED_SIZE(stride*uv_height, 0x1000); } - assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, buf.out_img_width)); + assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, sensor->frame_width)); assert(y_height/2 == uv_height); open = true; @@ -648,14 +645,14 @@ void SpectraCamera::config_bps(int idx, int request_id) { io_cfg[1].mem_handle[0] = buf_handle_yuv[idx]; io_cfg[1].mem_handle[1] = buf_handle_yuv[idx]; io_cfg[1].planes[0] = (struct cam_plane_cfg){ - .width = buf.out_img_width, - .height = buf.out_img_height, + .width = sensor->frame_width, + .height = sensor->frame_height, .plane_stride = stride, .slice_height = y_height, }; io_cfg[1].planes[1] = (struct cam_plane_cfg){ - .width = buf.out_img_width, - .height = buf.out_img_height / 2, + .width = sensor->frame_width, + .height = sensor->frame_height/2, .plane_stride = stride, .slice_height = uv_height, }; @@ -740,7 +737,7 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { bool is_raw = cc.output_type != ISP_IFE_PROCESSED; if (!is_raw) { if (init) { - buf_desc[0].length = build_initial_config((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches, buf.out_img_width, buf.out_img_height); + buf_desc[0].length = build_initial_config((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches); } else { buf_desc[0].length = build_update((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches); } @@ -847,14 +844,14 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { io_cfg[0].mem_handle[0] = buf_handle_yuv[idx]; io_cfg[0].mem_handle[1] = buf_handle_yuv[idx]; io_cfg[0].planes[0] = (struct cam_plane_cfg){ - .width = buf.out_img_width, - .height = buf.out_img_height, + .width = sensor->frame_width, + .height = sensor->frame_height, .plane_stride = stride, .slice_height = y_height, }; io_cfg[0].planes[1] = (struct cam_plane_cfg){ - .width = buf.out_img_width, - .height = buf.out_img_height / 2, + .width = sensor->frame_width, + .height = sensor->frame_height/2, .plane_stride = stride, .slice_height = uv_height, }; @@ -996,9 +993,6 @@ bool SpectraCamera::openSensor() { LOGD("-- Probing sensor %d", cc.camera_num); auto init_sensor_lambda = [this](SensorInfo *s) { - if (s->image_sensor == cereal::FrameData::ImageSensor::OS04C10 && cc.output_type == ISP_IFE_PROCESSED) { - ((OS04C10*)s)->ife_downscale_configure(); - } sensor.reset(s); return (sensors_init() == 0); }; @@ -1071,8 +1065,8 @@ void SpectraCamera::configISP() { .data[0] = (struct cam_isp_out_port_info){ .res_type = CAM_ISP_IFE_OUT_RES_FULL, .format = CAM_FORMAT_NV12, - .width = buf.out_img_width, - .height = buf.out_img_height + sensor->extra_height, + .width = sensor->frame_width, + .height = sensor->frame_height + sensor->extra_height, .comp_grp_id = 0x0, .split_point = 0x0, .secure_mode = 0x0, }, }; @@ -1147,8 +1141,8 @@ void SpectraCamera::configICP() { }, .out_res[0] = (struct cam_icp_res_info){ .format = 0x3, // YUV420NV12 - .width = buf.out_img_width, - .height = buf.out_img_height, + .width = sensor->frame_width, + .height = sensor->frame_height, .fps = 20, }, }; diff --git a/system/camerad/sensors/os04c10.cc b/system/camerad/sensors/os04c10.cc index 38be4ecca4..d008e1d07b 100644 --- a/system/camerad/sensors/os04c10.cc +++ b/system/camerad/sensors/os04c10.cc @@ -20,17 +20,6 @@ const uint32_t os04c10_analog_gains_reg[] = { } // namespace -void OS04C10::ife_downscale_configure() { - out_scale = 2; - - pixel_size_mm = 0.002; - frame_width = 2688; - frame_height = 1520; - exposure_time_max = 2352; - - init_reg_array.insert(init_reg_array.end(), std::begin(ife_downscale_override_array_os04c10), std::end(ife_downscale_override_array_os04c10)); -} - OS04C10::OS04C10() { image_sensor = cereal::FrameData::ImageSensor::OS04C10; bayer_pattern = CAM_ISP_PATTERN_BAYER_BGBGBG; diff --git a/system/camerad/sensors/os04c10_registers.h b/system/camerad/sensors/os04c10_registers.h index b501656090..8b1c78c69d 100644 --- a/system/camerad/sensors/os04c10_registers.h +++ b/system/camerad/sensors/os04c10_registers.h @@ -88,6 +88,8 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37c7, 0xa8}, {0x37da, 0x11}, {0x381f, 0x08}, + // {0x3829, 0x03}, + // {0x3832, 0x00}, {0x3881, 0x00}, {0x3888, 0x04}, {0x388b, 0x00}, @@ -197,6 +199,7 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x370b, 0x48}, {0x370c, 0x01}, {0x370f, 0x00}, + {0x3714, 0x28}, {0x3716, 0x04}, {0x3719, 0x11}, {0x371a, 0x1e}, @@ -228,6 +231,7 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37bd, 0x01}, {0x37bf, 0x26}, {0x37c0, 0x11}, + {0x37c2, 0x14}, {0x37cd, 0x19}, {0x37e0, 0x08}, {0x37e6, 0x04}, @@ -237,9 +241,14 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37d8, 0x02}, {0x37e2, 0x10}, {0x3739, 0x10}, + {0x3662, 0x08}, {0x37e4, 0x20}, {0x37e3, 0x08}, + {0x37d9, 0x04}, {0x4040, 0x00}, + {0x4041, 0x03}, + {0x4008, 0x01}, + {0x4009, 0x06}, // FSIN {0x3002, 0x22}, @@ -260,11 +269,20 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x8f}, {0x3806, 0x05}, {0x3807, 0xff}, + {0x3808, 0x05}, {0x3809, 0x40}, + {0x380a, 0x02}, {0x380b, 0xf8}, {0x3811, 0x08}, {0x3813, 0x08}, + {0x3814, 0x03}, {0x3815, 0x01}, + {0x3816, 0x03}, {0x3817, 0x01}, + {0x380c, 0x0b}, {0x380d, 0xac}, // HTS + {0x380e, 0x06}, {0x380f, 0x9c}, // VTS + + {0x3820, 0xb3}, + {0x3821, 0x01}, {0x3880, 0x00}, {0x3882, 0x20}, {0x3c91, 0x0b}, @@ -313,40 +331,4 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { // r {0x5104, 0x08}, {0x5105, 0xd6}, {0x5144, 0x08}, {0x5145, 0xd6}, - - {0x3714, 0x28}, - {0x37c2, 0x14}, - {0x3662, 0x08}, - {0x37d9, 0x04}, - {0x4041, 0x03}, - {0x4008, 0x01}, - {0x4009, 0x06}, - {0x3808, 0x05}, {0x3809, 0x40}, - {0x380a, 0x02}, {0x380b, 0xf8}, - {0x3814, 0x03}, - {0x3816, 0x03}, - {0x380c, 0x0b}, {0x380d, 0xac}, // HTS - {0x380e, 0x06}, {0x380f, 0x9c}, // VTS - {0x3820, 0xb3}, - {0x3821, 0x01}, -}; - -const struct i2c_random_wr_payload ife_downscale_override_array_os04c10[] = { - // OS04C10_AA_00_02_17_wAO_2688x1524_MIPI728Mbps_Linear12bit_20FPS_4Lane_MCLK24MHz - {0x3829, 0x03}, - {0x3714, 0x24}, - {0x37c2, 0x04}, - {0x3662, 0x10}, - {0x37d9, 0x08}, - {0x4041, 0x07}, - {0x4008, 0x02}, - {0x4009, 0x0d}, - {0x3808, 0x0a}, {0x3809, 0x80}, - {0x380a, 0x05}, {0x380b, 0xf0}, - {0x3814, 0x01}, - {0x3816, 0x01}, - {0x380c, 0x08}, {0x380d, 0x5c}, // HTS - {0x380e, 0x09}, {0x380f, 0x38}, // VTS - {0x3820, 0xb0}, - {0x3821, 0x00}, }; diff --git a/system/camerad/sensors/sensor.h b/system/camerad/sensors/sensor.h index d4be3cf036..c1131aafdf 100644 --- a/system/camerad/sensors/sensor.h +++ b/system/camerad/sensors/sensor.h @@ -29,7 +29,6 @@ public: uint32_t frame_stride; uint32_t frame_offset = 0; uint32_t extra_height = 0; - int out_scale = 1; int registers_offset = -1; int stats_offset = -1; int hdr_offset = -1; @@ -110,7 +109,6 @@ public: class OS04C10 : public SensorInfo { public: OS04C10(); - void ife_downscale_configure(); std::vector getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const override; float getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const override; int getSlaveAddress(int port) const override; From 8cefc00a6e67499eddf4fadb8eb4f1f491a50bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 21 Apr 2025 17:06:53 -0700 Subject: [PATCH 049/477] Tomb Raider 3 (#35042) * 92b64884-4506-4a03-87ad-33e1a177fe73/400 * faster lat --- selfdrive/modeld/modeld.py | 2 +- selfdrive/modeld/models/driving_policy.onnx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 1060b36d28..1d0facd133 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -41,7 +41,7 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' -LAT_SMOOTH_SECONDS = 0.3 +LAT_SMOOTH_SECONDS = 0.1 LONG_SMOOTH_SECONDS = 0.3 diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 3bf2e01b5e..df11e590dd 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa00a07a4307b0f1638a10ec33b651d8a102e38371289b744f43215c89dfd342 +oid sha256:84061882148485dabfdd23ee7e0925284a4370d0ae5be804d6099ae847352891 size 15578328 From 0102a1c704dcf3b5dc00e98fa1390a993507acb2 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 22 Apr 2025 00:03:06 -0400 Subject: [PATCH 050/477] DM: Use `carControl.latActive` for MADS DM monitoring (#845) --- selfdrive/monitoring/dmonitoringd.py | 2 +- selfdrive/monitoring/helpers.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/monitoring/dmonitoringd.py b/selfdrive/monitoring/dmonitoringd.py index d0d28ebac1..71733f99fc 100755 --- a/selfdrive/monitoring/dmonitoringd.py +++ b/selfdrive/monitoring/dmonitoringd.py @@ -11,7 +11,7 @@ def dmonitoringd_thread(): params = Params() pm = messaging.PubMaster(['driverMonitoringState']) sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'selfdriveState', 'modelV2', - 'selfdriveStateSP'], poll='driverStateV2') + 'carControl'], poll='driverStateV2') DM = DriverMonitoring(rhd_saved=params.get_bool("IsRhdDetected"), always_on=params.get_bool("AlwaysOnDM")) diff --git a/selfdrive/monitoring/helpers.py b/selfdrive/monitoring/helpers.py index 0ff7a52a92..67776994f0 100644 --- a/selfdrive/monitoring/helpers.py +++ b/selfdrive/monitoring/helpers.py @@ -403,13 +403,13 @@ class DriverMonitoring: driver_state=sm['driverStateV2'], cal_rpy=sm['liveCalibration'].rpyCalib, car_speed=sm['carState'].vEgo, - op_engaged=sm['selfdriveState'].enabled or sm['selfdriveStateSP'].mads.active + op_engaged=sm['selfdriveState'].enabled or sm['carControl'].latActive ) # Update distraction events self._update_events( driver_engaged=sm['carState'].steeringPressed or sm['carState'].gasPressed, - op_engaged=sm['selfdriveState'].enabled or sm['selfdriveStateSP'].mads.active, + op_engaged=sm['selfdriveState'].enabled or sm['carControl'].latActive, standstill=sm['carState'].standstill, wrong_gear=sm['carState'].gearShifter in [car.CarState.GearShifter.reverse, car.CarState.GearShifter.park], car_speed=sm['carState'].vEgo From fd0d4a5aab7d3c34fb649f5ca330a5871390168d Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Tue, 22 Apr 2025 14:54:18 +0200 Subject: [PATCH 051/477] ci: fixes for trigger conditions on sync (#847) pull_request_target is needed for security, but at the same time it makes life a little harder because the ref is different so we need to do some work to make sure we get what we are supposed to get --- .../workflows/sunnypilot-build-prebuilt.yaml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sunnypilot-build-prebuilt.yaml b/.github/workflows/sunnypilot-build-prebuilt.yaml index 156e10515b..c203edfa75 100644 --- a/.github/workflows/sunnypilot-build-prebuilt.yaml +++ b/.github/workflows/sunnypilot-build-prebuilt.yaml @@ -54,11 +54,13 @@ jobs: version: ${{ steps.set-env.outputs.version }} extra_version_identifier: ${{ steps.set-env.outputs.extra_version_identifier }} commit_sha: ${{ steps.set-env.outputs.commit_sha }} - if: always() && !failure() && !cancelled() + if: ${{ (always() && !failure() && !cancelled()) && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }} steps: - uses: actions/checkout@v4 with: submodules: recursive + ref: ${{ github.head_ref || github.ref_name }} + repository: ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.head.repo.full_name || github.repository }} - run: git lfs pull - name: Cache SCons @@ -75,15 +77,16 @@ jobs: scons-${{ runner.os }}-${{ runner.arch }} - name: Set Configuration + if: (!github.event.pull_request.head.repo.fork) run: | - if [[ "${{ github.ref_name }}" == "${{ env.DEV_C3_SOURCE_BRANCH }}" ]]; then + if [[ "${{ github.head_ref || github.ref_name }}" == "${{ env.DEV_C3_SOURCE_BRANCH }}" ]]; then # Dev configuration echo "BRANCH_TYPE=dev" >> $GITHUB_ENV echo "NEW_BRANCH=${{ env.DEV_TARGET_BRANCH }}" >> $GITHUB_ENV echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV echo "EXTRA_VERSION_IDENTIFIER=${{ github.run_number }}" >> $GITHUB_ENV - elif [[ "${{ github.ref_name }}" == "${{ env.MASTER_BRANCH }}" || "${{ github.ref_name }}" == "${{ env.MASTER_NEW_BRANCH }}" ]]; then + elif [[ "${{ github.head_ref || github.ref_name }}" == "${{ env.MASTER_BRANCH }}" || "${{ github.ref_name }}" == "${{ env.MASTER_NEW_BRANCH }}" ]]; then # Master configuration echo "BRANCH_TYPE=master" >> $GITHUB_ENV echo "NEW_BRANCH=${{ env.STAGING_TARGET_BRANCH }}" >> $GITHUB_ENV @@ -103,6 +106,13 @@ jobs: echo "NEW_BRANCH=${{ github.head_ref || github.ref_name }}-prebuilt" >> $GITHUB_ENV echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV fi + + - name: Set Configuration (only forks) + if: (github.event.pull_request.head.repo.fork) + run: | + echo "BRANCH_TYPE=dispatch" >> $GITHUB_ENV + echo "NEW_BRANCH=${{ github.head_ref || github.ref_name }}-fork-prebuilt" >> $GITHUB_ENV + echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV - name: Set environment variables id: set-env @@ -258,7 +268,7 @@ jobs: notify: needs: [ build, publish ] runs-on: ubuntu-24.04 - if: ${{ (always() && !failure() && !cancelled()) }} + if: ${{ (always() && !failure() && !cancelled()) && (!contains(github.event_name, 'pull_request') || (github.event.action == 'labeled' && github.event.label.name == 'prebuilt')) }} steps: - uses: actions/checkout@v4 - name: Setup Alpine Linux environment From 23524e203826ca944ffbff9789ec9765ba2b6c8e Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 22 Apr 2025 15:28:54 +0100 Subject: [PATCH 052/477] ui(raylib): reduce spinner rotation artifact (#35048) * ui(raylib): reduce spinner rotation artifact A visual artifact (white pixels) appeared on the edge of the rotating spinner track texture, likely due to RGB color bleed during bilinear filtering in Raylib. Pre-multiplying the alpha channel of the spinner track image using `rl.image_alpha_premultiply` significantly reduces the visibility of the artifact. * lint --- system/ui/lib/application.py | 4 +++- system/ui/spinner.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 470396bbc7..23f9316c3a 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -50,9 +50,11 @@ class GuiApplication: self._set_styles() self._load_fonts() - def load_texture_from_image(self, file_name: str, width: int, height: int): + def load_texture_from_image(self, file_name: str, width: int, height: int, alpha_premultiply = False): """Load and resize a texture, storing it for later automatic unloading.""" image = rl.load_image(file_name) + if alpha_premultiply: + rl.image_alpha_premultiply(image) rl.image_resize(image, width, height) texture = rl.load_texture_from_image(image) # Set texture filtering to smooth the result diff --git a/system/ui/spinner.py b/system/ui/spinner.py index adfb604de9..f3724cbd1e 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -23,7 +23,8 @@ def clamp(value, min_value, max_value): class Spinner: def __init__(self): self._comma_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_comma.png"), TEXTURE_SIZE, TEXTURE_SIZE) - self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE) + self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE, + alpha_premultiply=True) self._rotation = 0.0 self._text: str = "" self._progress: int | None = None From 651ff78cb051280513c297ac1ab356eb164116e8 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 22 Apr 2025 16:07:05 +0100 Subject: [PATCH 053/477] ui(raylib): increase font size and wrap text in spinner (#35049) - Wrap text onto separate lines - Increase font size to be closer to Qt - Remove extra letter spacing - 0.0 should use font default spacing, and this is used in `wrap_text` Will fix vertical alignment separately, as both the text and progress bar layouts need to be considered --- system/ui/spinner.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index f3724cbd1e..fd4264be6c 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -5,14 +5,17 @@ import threading from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.text import wrap_text # Constants PROGRESS_BAR_WIDTH = 1000 PROGRESS_BAR_HEIGHT = 20 DEGREES_PER_SECOND = 360.0 # one full rotation per second -MARGIN = 200 +MARGIN_H = 100 +MARGIN_V = 200 TEXTURE_SIZE = 360 -FONT_SIZE = 80 +FONT_SIZE = 88 +LINE_HEIGHT = 96 DARKGRAY = (55, 55, 55, 255) @@ -26,18 +29,18 @@ class Spinner: self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True) self._rotation = 0.0 - self._text: str = "" self._progress: int | None = None + self._wrapped_lines: list[str] = [] self._lock = threading.Lock() def set_text(self, text: str) -> None: with self._lock: if text.isdigit(): self._progress = clamp(int(text), 0, 100) - self._text = "" + self._wrapped_lines = [] else: self._progress = None - self._text = text + self._wrapped_lines = wrap_text(text, FONT_SIZE, gui_app.width - MARGIN_H) def render(self): center = rl.Vector2(gui_app.width / 2.0, gui_app.height / 2.0) @@ -54,10 +57,10 @@ class Spinner: rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE) # Display progress bar or text based on user input - y_pos = rl.get_screen_height() - MARGIN - PROGRESS_BAR_HEIGHT + y_pos = rl.get_screen_height() - MARGIN_V - PROGRESS_BAR_HEIGHT with self._lock: progress = self._progress - text = self._text + wrapped_lines = self._wrapped_lines if progress is not None: bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT) @@ -65,10 +68,11 @@ class Spinner: bar.width *= progress / 100.0 rl.draw_rectangle_rounded(bar, 1, 10, rl.WHITE) - elif text: - text_size = rl.measure_text_ex(gui_app.font(), text, FONT_SIZE, 1.0) - rl.draw_text_ex(gui_app.font(), text, - rl.Vector2(center.x - text_size.x / 2, y_pos), FONT_SIZE, 1.0, rl.WHITE) + elif wrapped_lines: + for i, line in enumerate(wrapped_lines): + text_size = rl.measure_text_ex(gui_app.font(), line, FONT_SIZE, 0.0) + rl.draw_text_ex(gui_app.font(), line, rl.Vector2(center.x - text_size.x / 2, y_pos + i * LINE_HEIGHT), + FONT_SIZE, 0.0, rl.WHITE) if __name__ == "__main__": From c9259a9bcfd86f77c0d336b470c1f72cbfd073e8 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 22 Apr 2025 20:54:36 +0100 Subject: [PATCH 054/477] ui(raylib): update spinner vertical pos (#35051) Adjust the spinner vertical position when displaying text or a progress bar - When displaying the progress bar, center the comma logo and spinner in the middle of the screen - When displaying text, center the entire content vertically Also updated `wrap_text` to not include an empty line in the array if it's the first line, so that `wrap_text("")` always returns `[]` --- system/ui/spinner.py | 23 ++++++++++++++++------- system/ui/text.py | 4 +++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index fd4264be6c..9c1882cf5f 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -12,7 +12,6 @@ PROGRESS_BAR_WIDTH = 1000 PROGRESS_BAR_HEIGHT = 20 DEGREES_PER_SECOND = 360.0 # one full rotation per second MARGIN_H = 100 -MARGIN_V = 200 TEXTURE_SIZE = 360 FONT_SIZE = 88 LINE_HEIGHT = 96 @@ -43,7 +42,22 @@ class Spinner: self._wrapped_lines = wrap_text(text, FONT_SIZE, gui_app.width - MARGIN_H) def render(self): - center = rl.Vector2(gui_app.width / 2.0, gui_app.height / 2.0) + with self._lock: + progress = self._progress + wrapped_lines = self._wrapped_lines + + if wrapped_lines: + # Calculate total height required for spinner and text + spacing = 50 + total_height = TEXTURE_SIZE + spacing + len(wrapped_lines) * LINE_HEIGHT + center_y = (gui_app.height - total_height) / 2.0 + TEXTURE_SIZE / 2.0 + else: + # Center spinner vertically + spacing = 150 + center_y = gui_app.height / 2.0 + y_pos = center_y + TEXTURE_SIZE / 2.0 + spacing + + center = rl.Vector2(gui_app.width / 2.0, center_y) spinner_origin = rl.Vector2(TEXTURE_SIZE / 2.0, TEXTURE_SIZE / 2.0) comma_position = rl.Vector2(center.x - TEXTURE_SIZE / 2.0, center.y - TEXTURE_SIZE / 2.0) @@ -57,11 +71,6 @@ class Spinner: rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE) # Display progress bar or text based on user input - y_pos = rl.get_screen_height() - MARGIN_V - PROGRESS_BAR_HEIGHT - with self._lock: - progress = self._progress - wrapped_lines = self._wrapped_lines - if progress is not None: bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT) rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY) diff --git a/system/ui/text.py b/system/ui/text.py index 4ea886ba1e..79c20ce8dc 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -21,7 +21,9 @@ def wrap_text(text, font_size, max_width): for paragraph in text.split("\n"): if not paragraph.strip(): - lines.append("") + # Don't add empty lines first, ensuring wrap_text("") returns [] + if lines: + lines.append("") continue indent = re.match(r"^\s*", paragraph).group() current_line = indent From f9767d265010101f123dcdc261206cb7be8bb5d8 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 22 Apr 2025 22:07:47 +0100 Subject: [PATCH 055/477] ui(raylib): "exit" btn in text window on PC (#35052) * ui(raylib): "exit" btn in text window on PC * unused import --- system/ui/text.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/ui/text.py b/system/ui/text.py index 79c20ce8dc..9c7101dfe8 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import re import pyray as rl -from openpilot.system.hardware import HARDWARE +from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app @@ -58,9 +58,12 @@ class TextWindow: rl.end_scissor_mode() button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) - ret = gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT) + ret = gui_button(button_bounds, "Exit" if PC else "Reboot", button_style=ButtonStyle.TRANSPARENT) if ret: - HARDWARE.reboot() + if PC: + gui_app.close() + else: + HARDWARE.reboot() return ret From 754f5aa95590f7bc98c8f4f8fbe3f7f2a0a18e96 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 22 Apr 2025 22:30:20 +0100 Subject: [PATCH 056/477] Revert "ui(raylib): "exit" btn in text window on PC (#35052)" This reverts commit f9767d265010101f123dcdc261206cb7be8bb5d8. --- system/ui/text.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/system/ui/text.py b/system/ui/text.py index 9c7101dfe8..79c20ce8dc 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import re import pyray as rl -from openpilot.system.hardware import HARDWARE, PC +from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app @@ -58,12 +58,9 @@ class TextWindow: rl.end_scissor_mode() button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) - ret = gui_button(button_bounds, "Exit" if PC else "Reboot", button_style=ButtonStyle.TRANSPARENT) + ret = gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT) if ret: - if PC: - gui_app.close() - else: - HARDWARE.reboot() + HARDWARE.reboot() return ret From 7b5468b2e8457856acbf3213be9ef0379a10597e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Tue, 22 Apr 2025 14:50:03 -0700 Subject: [PATCH 057/477] plotjuggler: support relative paths (#35054) * Support relative paths * Abspath for rlog too --- tools/plotjuggler/juggle.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/plotjuggler/juggle.py b/tools/plotjuggler/juggle.py index f66782d6f3..34f33d1959 100755 --- a/tools/plotjuggler/juggle.py +++ b/tools/plotjuggler/juggle.py @@ -61,13 +61,15 @@ def start_juggler(fn=None, dbc=None, layout=None, route_or_segment_name=None, pl env["BASEDIR"] = BASEDIR env["PATH"] = f"{INSTALL_DIR}:{os.getenv('PATH', '')}" if dbc: + if os.path.exists(dbc): + dbc = os.path.abspath(dbc) env["DBC_NAME"] = dbc extra_args = "" if fn is not None: - extra_args += f" -d {fn}" + extra_args += f" -d {os.path.abspath(fn)}" if layout is not None: - extra_args += f" -l {layout}" + extra_args += f" -l {os.path.abspath(layout)}" if route_or_segment_name is not None: extra_args += f" --window_title \"{route_or_segment_name}{f' ({platform})' if platform is not None else ''}\"" From 281a72780b8e8b97fb12d983a29edc1f4f242aeb Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 23 Apr 2025 14:06:09 +0100 Subject: [PATCH 058/477] Reapply "ui(raylib): "exit" btn in text window on PC" (#35053) * ui(raylib): "exit" btn in text window on PC * unused import * request close * this was missing --- system/ui/lib/application.py | 6 +++++- system/ui/text.py | 10 +++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 23f9316c3a..f5a238b81e 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -38,6 +38,10 @@ class GuiApplication: self._textures: list[rl.Texture] = [] self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() + self._window_close_requested = False + + def request_close(self): + self._window_close_requested = True def init_window(self, title: str, fps: int=DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit @@ -80,7 +84,7 @@ class GuiApplication: rl.close_window() def render(self): - while not rl.window_should_close(): + while not (self._window_close_requested or rl.window_should_close()): rl.begin_drawing() rl.clear_background(rl.BLACK) diff --git a/system/ui/text.py b/system/ui/text.py index 79c20ce8dc..1337887d9b 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import re import pyray as rl -from openpilot.system.hardware import HARDWARE +from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app @@ -58,9 +58,12 @@ class TextWindow: rl.end_scissor_mode() button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) - ret = gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT) + ret = gui_button(button_bounds, "Exit" if PC else "Reboot", button_style=ButtonStyle.TRANSPARENT) if ret: - HARDWARE.reboot() + if PC: + gui_app.request_close() + else: + HARDWARE.reboot() return ret @@ -69,6 +72,7 @@ def show_text_in_window(text: str): text_window = TextWindow(text) for _ in gui_app.render(): text_window.render() + gui_app.close() if __name__ == "__main__": From a38dcbb3fed278da25c3b8f3d538bdc43fab65d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Wed, 23 Apr 2025 14:15:06 -0700 Subject: [PATCH 059/477] Disable low speed steer in modeld (#35056) * disable low speed steer * No steer under --- selfdrive/modeld/modeld.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 1d0facd133..b89c8bad57 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -43,10 +43,11 @@ POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.p LAT_SMOOTH_SECONDS = 0.1 LONG_SMOOTH_SECONDS = 0.3 +MIN_LAT_CONTROL_SPEED = 0.3 def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, - lat_action_t: float, long_action_t: float,) -> log.ModelDataV2.Action: + lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action: plan = model_output['plan'][0] desired_accel, should_stop = get_accel_from_plan(plan[:,Plan.VELOCITY][:,0], plan[:,Plan.ACCELERATION][:,0], @@ -55,7 +56,10 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log. desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS) desired_curvature = model_output['desired_curvature'][0, 0] - desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) + if v_ego > MIN_LAT_CONTROL_SPEED: + desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) + else: + desired_curvature = prev_action.desiredCurvature return log.ModelDataV2.Action(desiredCurvature=float(desired_curvature), desiredAcceleration=float(desired_accel), @@ -331,7 +335,7 @@ def main(demo=False): drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL) + action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego) prev_action = action fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, From 63a188d2e01b5891225806b887b315b114ded3b0 Mon Sep 17 00:00:00 2001 From: Warren Togami Date: Wed, 23 Apr 2025 20:23:40 -0500 Subject: [PATCH 060/477] TOYOTA_RAV4_PRIME NNLC tuning gen 1 (#850) neural_network_data: TOYOTA_RAV4_PRIME NNLC tuning gen 1 --- sunnypilot/neural_network_data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sunnypilot/neural_network_data b/sunnypilot/neural_network_data index 61dbe23f5d..16946bfd9c 160000 --- a/sunnypilot/neural_network_data +++ b/sunnypilot/neural_network_data @@ -1 +1 @@ -Subproject commit 61dbe23f5d6e3086cc549f7c598090256ad6bd87 +Subproject commit 16946bfd9cca1a33e8b609d10ca3d1a0089d38e2 From e85d833a800838bb11e6abc625f4130b5114ad9a Mon Sep 17 00:00:00 2001 From: ZwX1616 Date: Wed, 23 Apr 2025 21:09:22 -0700 Subject: [PATCH 061/477] Revert "Revert "OS04C10: use IFE downscaler for road cameras"" (#35047) * Revert "Revert "OS04C10: use IFE downscaler for road cameras" (#35046)" This reverts commit aaaa2d0dd064364d647fb381cc488fe6fe4d44e2. * fixed * patch it here * surely --- system/camerad/cameras/camera_common.cc | 3 -- system/camerad/cameras/camera_common.h | 2 +- system/camerad/cameras/camera_qcom2.cc | 10 +++--- system/camerad/cameras/ife.h | 18 +++++----- system/camerad/cameras/spectra.cc | 40 +++++++++++++--------- system/camerad/sensors/os04c10.cc | 11 ++++++ system/camerad/sensors/os04c10_registers.h | 22 ++++++++++-- system/camerad/sensors/sensor.h | 2 ++ 8 files changed, 71 insertions(+), 37 deletions(-) diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index f6c2681dea..1f6ad9b4be 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -26,9 +26,6 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, SpectraCamera * LOGD("allocated %d CL buffers", frame_buf_count); } - out_img_width = sensor->frame_width; - out_img_height = sensor->hdr_offset > 0 ? (sensor->frame_height - sensor->hdr_offset) / 2 : sensor->frame_height; - // the encoder HW tells us the size it wants after setting it up. // TODO: VENUS_BUFFER_SIZE should give the size, but it's too small. dependent on encoder settings? size_t nv12_size = (out_img_width <= 1344 ? 2900 : 2346)*cam->stride; diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h index c0426678b6..c26859cbc4 100644 --- a/system/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -32,7 +32,7 @@ public: VisionBuf *cur_yuv_buf; VisionBuf *cur_camera_buf; std::unique_ptr camera_bufs_raw; - int out_img_width, out_img_height; + uint32_t out_img_width, out_img_height; CameraBuf() = default; ~CameraBuf(); diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 18a42f05d8..8c4602bb31 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -73,7 +73,7 @@ void CameraState::init(VisionIpcServer *v, cl_device_id device_id, cl_context ct if (!camera.enabled) return; - fl_pix = camera.cc.focal_len / camera.sensor->pixel_size_mm; + fl_pix = camera.cc.focal_len / camera.sensor->pixel_size_mm / camera.sensor->out_scale; set_exposure_rect(); dc_gain_weight = camera.sensor->dc_gain_min_weight; @@ -107,10 +107,10 @@ void CameraState::set_exposure_rect() { float fl_ref = ae_target.second; ae_xywh = (Rect){ - std::max(0, camera.buf.out_img_width / 2 - (int)(fl_pix / fl_ref * xywh_ref.w / 2)), - std::max(0, camera.buf.out_img_height / 2 - (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))), - std::min((int)(fl_pix / fl_ref * xywh_ref.w), camera.buf.out_img_width / 2 + (int)(fl_pix / fl_ref * xywh_ref.w / 2)), - std::min((int)(fl_pix / fl_ref * xywh_ref.h), camera.buf.out_img_height / 2 + (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))) + std::max(0, (int)camera.buf.out_img_width / 2 - (int)(fl_pix / fl_ref * xywh_ref.w / 2)), + std::max(0, (int)camera.buf.out_img_height / 2 - (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))), + std::min((int)(fl_pix / fl_ref * xywh_ref.w), (int)camera.buf.out_img_width / 2 + (int)(fl_pix / fl_ref * xywh_ref.w / 2)), + std::min((int)(fl_pix / fl_ref * xywh_ref.h), (int)camera.buf.out_img_height / 2 + (int)(fl_pix / fl_ref * (h_ref / 2 - xywh_ref.y))) }; } diff --git a/system/camerad/cameras/ife.h b/system/camerad/cameras/ife.h index 49737f2db7..fd87d2baa4 100644 --- a/system/camerad/cameras/ife.h +++ b/system/camerad/cameras/ife.h @@ -105,7 +105,7 @@ int build_update(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std: } -int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std::vector &patches) { +int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo *s, std::vector &patches, uint32_t out_width, uint32_t out_height) { uint8_t *start = dst; // start with the every frame config @@ -185,12 +185,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo // output size/scaling dst += write_cont(dst, 0xa3c, { 0x00000003, - ((s->frame_width - 1) << 16) | (s->frame_width - 1), + ((out_width - 1) << 16) | (s->frame_width - 1), 0x30036666, 0x00000000, 0x00000000, s->frame_width - 1, - ((s->frame_height - 1) << 16) | (s->frame_height - 1), + ((out_height - 1) << 16) | (s->frame_height - 1), 0x30036666, 0x00000000, 0x00000000, @@ -198,12 +198,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo }); dst += write_cont(dst, 0xa68, { 0x00000003, - ((s->frame_width/2 - 1) << 16) | (s->frame_width - 1), + ((out_width / 2 - 1) << 16) | (s->frame_width - 1), 0x3006cccc, 0x00000000, 0x00000000, s->frame_width - 1, - ((s->frame_height/2 - 1) << 16) | (s->frame_height - 1), + ((out_height / 2 - 1) << 16) | (s->frame_height - 1), 0x3006cccc, 0x00000000, 0x00000000, @@ -212,12 +212,12 @@ int build_initial_config(uint8_t *dst, const CameraConfig cam, const SensorInfo // cropping dst += write_cont(dst, 0xe10, { - s->frame_height - 1, - s->frame_width - 1, + out_height - 1, + out_width - 1, }); dst += write_cont(dst, 0xe30, { - s->frame_height/2 - 1, - s->frame_width - 1, + out_height / 2 - 1, + out_width - 1, }); dst += write_cont(dst, 0xe18, { 0x0ff00000, diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc index 4535078771..47ae9061f4 100644 --- a/system/camerad/cameras/spectra.cc +++ b/system/camerad/cameras/spectra.cc @@ -281,18 +281,21 @@ void SpectraCamera::camera_open(VisionIpcServer *v, cl_device_id device_id, cl_c if (!enabled) return; + buf.out_img_width = sensor->frame_width / sensor->out_scale; + buf.out_img_height = (sensor->hdr_offset > 0 ? (sensor->frame_height - sensor->hdr_offset) / 2 : sensor->frame_height) / sensor->out_scale; + // size is driven by all the HW that handles frames, // the video encoder has certain alignment requirements in this case - stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, sensor->frame_width); - y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, sensor->frame_height); - uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, sensor->frame_height); + stride = VENUS_Y_STRIDE(COLOR_FMT_NV12, buf.out_img_width); + y_height = VENUS_Y_SCANLINES(COLOR_FMT_NV12, buf.out_img_height); + uv_height = VENUS_UV_SCANLINES(COLOR_FMT_NV12, buf.out_img_height); uv_offset = stride*y_height; yuv_size = uv_offset + stride*uv_height; if (cc.output_type != ISP_RAW_OUTPUT) { uv_offset = ALIGNED_SIZE(uv_offset, 0x1000); yuv_size = uv_offset + ALIGNED_SIZE(stride*uv_height, 0x1000); } - assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, sensor->frame_width)); + assert(stride == VENUS_UV_STRIDE(COLOR_FMT_NV12, buf.out_img_width)); assert(y_height/2 == uv_height); open = true; @@ -645,14 +648,14 @@ void SpectraCamera::config_bps(int idx, int request_id) { io_cfg[1].mem_handle[0] = buf_handle_yuv[idx]; io_cfg[1].mem_handle[1] = buf_handle_yuv[idx]; io_cfg[1].planes[0] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height, + .width = buf.out_img_width, + .height = buf.out_img_height, .plane_stride = stride, .slice_height = y_height, }; io_cfg[1].planes[1] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height/2, + .width = buf.out_img_width, + .height = buf.out_img_height / 2, .plane_stride = stride, .slice_height = uv_height, }; @@ -737,7 +740,7 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { bool is_raw = cc.output_type != ISP_IFE_PROCESSED; if (!is_raw) { if (init) { - buf_desc[0].length = build_initial_config((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches); + buf_desc[0].length = build_initial_config((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches, buf.out_img_width, buf.out_img_height); } else { buf_desc[0].length = build_update((unsigned char*)ife_cmd.ptr + buf_desc[0].offset, cc, sensor.get(), patches); } @@ -844,14 +847,14 @@ void SpectraCamera::config_ife(int idx, int request_id, bool init) { io_cfg[0].mem_handle[0] = buf_handle_yuv[idx]; io_cfg[0].mem_handle[1] = buf_handle_yuv[idx]; io_cfg[0].planes[0] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height, + .width = buf.out_img_width, + .height = buf.out_img_height, .plane_stride = stride, .slice_height = y_height, }; io_cfg[0].planes[1] = (struct cam_plane_cfg){ - .width = sensor->frame_width, - .height = sensor->frame_height/2, + .width = buf.out_img_width, + .height = buf.out_img_height / 2, .plane_stride = stride, .slice_height = uv_height, }; @@ -993,6 +996,9 @@ bool SpectraCamera::openSensor() { LOGD("-- Probing sensor %d", cc.camera_num); auto init_sensor_lambda = [this](SensorInfo *s) { + if (s->image_sensor == cereal::FrameData::ImageSensor::OS04C10 && cc.output_type == ISP_IFE_PROCESSED) { + ((OS04C10*)s)->ife_downscale_configure(); + } sensor.reset(s); return (sensors_init() == 0); }; @@ -1065,8 +1071,8 @@ void SpectraCamera::configISP() { .data[0] = (struct cam_isp_out_port_info){ .res_type = CAM_ISP_IFE_OUT_RES_FULL, .format = CAM_FORMAT_NV12, - .width = sensor->frame_width, - .height = sensor->frame_height + sensor->extra_height, + .width = buf.out_img_width, + .height = buf.out_img_height + sensor->extra_height, .comp_grp_id = 0x0, .split_point = 0x0, .secure_mode = 0x0, }, }; @@ -1141,8 +1147,8 @@ void SpectraCamera::configICP() { }, .out_res[0] = (struct cam_icp_res_info){ .format = 0x3, // YUV420NV12 - .width = sensor->frame_width, - .height = sensor->frame_height, + .width = buf.out_img_width, + .height = buf.out_img_height, .fps = 20, }, }; diff --git a/system/camerad/sensors/os04c10.cc b/system/camerad/sensors/os04c10.cc index d008e1d07b..38be4ecca4 100644 --- a/system/camerad/sensors/os04c10.cc +++ b/system/camerad/sensors/os04c10.cc @@ -20,6 +20,17 @@ const uint32_t os04c10_analog_gains_reg[] = { } // namespace +void OS04C10::ife_downscale_configure() { + out_scale = 2; + + pixel_size_mm = 0.002; + frame_width = 2688; + frame_height = 1520; + exposure_time_max = 2352; + + init_reg_array.insert(init_reg_array.end(), std::begin(ife_downscale_override_array_os04c10), std::end(ife_downscale_override_array_os04c10)); +} + OS04C10::OS04C10() { image_sensor = cereal::FrameData::ImageSensor::OS04C10; bayer_pattern = CAM_ISP_PATTERN_BAYER_BGBGBG; diff --git a/system/camerad/sensors/os04c10_registers.h b/system/camerad/sensors/os04c10_registers.h index 8b1c78c69d..7cd9e97be5 100644 --- a/system/camerad/sensors/os04c10_registers.h +++ b/system/camerad/sensors/os04c10_registers.h @@ -88,8 +88,6 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x37c7, 0xa8}, {0x37da, 0x11}, {0x381f, 0x08}, - // {0x3829, 0x03}, - // {0x3832, 0x00}, {0x3881, 0x00}, {0x3888, 0x04}, {0x388b, 0x00}, @@ -332,3 +330,23 @@ const struct i2c_random_wr_payload init_array_os04c10[] = { {0x5104, 0x08}, {0x5105, 0xd6}, {0x5144, 0x08}, {0x5145, 0xd6}, }; + +const struct i2c_random_wr_payload ife_downscale_override_array_os04c10[] = { + // OS04C10_AA_00_02_17_wAO_2688x1524_MIPI728Mbps_Linear12bit_20FPS_4Lane_MCLK24MHz + {0x3c8c, 0x40}, + {0x3714, 0x24}, + {0x37c2, 0x04}, + {0x3662, 0x10}, + {0x37d9, 0x08}, + {0x4041, 0x07}, + {0x4008, 0x02}, + {0x4009, 0x0d}, + {0x3808, 0x0a}, {0x3809, 0x80}, + {0x380a, 0x05}, {0x380b, 0xf0}, + {0x3814, 0x01}, + {0x3816, 0x01}, + {0x380c, 0x08}, {0x380d, 0x5c}, // HTS + {0x380e, 0x09}, {0x380f, 0x38}, // VTS + {0x3820, 0xb0}, + {0x3821, 0x00}, +}; diff --git a/system/camerad/sensors/sensor.h b/system/camerad/sensors/sensor.h index c1131aafdf..d4be3cf036 100644 --- a/system/camerad/sensors/sensor.h +++ b/system/camerad/sensors/sensor.h @@ -29,6 +29,7 @@ public: uint32_t frame_stride; uint32_t frame_offset = 0; uint32_t extra_height = 0; + int out_scale = 1; int registers_offset = -1; int stats_offset = -1; int hdr_offset = -1; @@ -109,6 +110,7 @@ public: class OS04C10 : public SensorInfo { public: OS04C10(); + void ife_downscale_configure(); std::vector getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const override; float getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const override; int getSlaveAddress(int port) const override; From 196848c023bddea801994699eb1d92409638f989 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 24 Apr 2025 15:55:15 +0100 Subject: [PATCH 062/477] ui(raylib): set display power and brightness on init (#35060) * add set_display_power to hardware.py * set display power and brightness on app init --- system/hardware/base.py | 4 ++++ system/hardware/pc/hardware.py | 3 +++ system/hardware/tici/hardware.py | 7 +++++++ system/ui/lib/application.py | 4 ++++ 4 files changed, 18 insertions(+) diff --git a/system/hardware/base.py b/system/hardware/base.py index c7c765f20a..f2c627d339 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -130,6 +130,10 @@ class HardwareBase(ABC): def get_thermal_config(self): return ThermalConfig() + @abstractmethod + def set_display_power(self, on: bool): + pass + @abstractmethod def set_screen_brightness(self, percentage): pass diff --git a/system/hardware/pc/hardware.py b/system/hardware/pc/hardware.py index 017a449c90..091f6a9b25 100644 --- a/system/hardware/pc/hardware.py +++ b/system/hardware/pc/hardware.py @@ -53,6 +53,9 @@ class Pc(HardwareBase): def shutdown(self): print("SHUTDOWN!") + def set_display_power(self, on): + pass + def set_screen_brightness(self, percentage): pass diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 15480bab50..ffa852403f 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -341,6 +341,13 @@ class Tici(HardwareBase): exhaust=exhaust, case=case) + def set_display_power(self, on): + try: + with open("/sys/class/backlight/panel0-backlight/bl_power", "w") as f: + f.write("0" if on else "4") + except Exception: + pass + def set_screen_brightness(self, percentage): try: with open("/sys/class/backlight/panel0-backlight/max_brightness") as f: diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index f5a238b81e..0b918d036c 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -5,6 +5,7 @@ import pyray as rl from enum import IntEnum from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog +from openpilot.system.hardware import HARDWARE DEFAULT_FPS = 60 FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops @@ -46,6 +47,9 @@ class GuiApplication: def init_window(self, title: str, fps: int=DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit + HARDWARE.set_display_power(True) + HARDWARE.set_screen_brightness(65) + rl.set_config_flags(rl.ConfigFlags.FLAG_MSAA_4X_HINT | rl.ConfigFlags.FLAG_VSYNC_HINT) rl.init_window(self._width, self._height, title) rl.set_target_fps(fps) From 8088438bd6d41311b27b6a5308c2a8ab10fd624d Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 24 Apr 2025 20:18:31 +0100 Subject: [PATCH 063/477] hardware: make set_display_power not abstract (#35061) hardware: make set_dispaly_power not abstract follow-up to https://github.com/commaai/openpilot/pull/35060#discussion_r2058829397 --- system/hardware/base.py | 1 - system/hardware/pc/hardware.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/system/hardware/base.py b/system/hardware/base.py index f2c627d339..1e3b94e44e 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -130,7 +130,6 @@ class HardwareBase(ABC): def get_thermal_config(self): return ThermalConfig() - @abstractmethod def set_display_power(self, on: bool): pass diff --git a/system/hardware/pc/hardware.py b/system/hardware/pc/hardware.py index 091f6a9b25..017a449c90 100644 --- a/system/hardware/pc/hardware.py +++ b/system/hardware/pc/hardware.py @@ -53,9 +53,6 @@ class Pc(HardwareBase): def shutdown(self): print("SHUTDOWN!") - def set_display_power(self, on): - pass - def set_screen_brightness(self, percentage): pass From 7c223e5586bc4ad8c21f1879fc3a0b73df6bd549 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 24 Apr 2025 21:00:48 +0100 Subject: [PATCH 064/477] ui(raylib): decrease target FPS to 30 (#35062) --- system/ui/lib/application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 0b918d036c..a3e209c394 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -7,7 +7,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE -DEFAULT_FPS = 60 +DEFAULT_FPS = 30 FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions @@ -44,7 +44,7 @@ class GuiApplication: def request_close(self): self._window_close_requested = True - def init_window(self, title: str, fps: int=DEFAULT_FPS): + def init_window(self, title: str, fps: int = DEFAULT_FPS): atexit.register(self.close) # Automatically call close() on exit HARDWARE.set_display_power(True) From 3ee2882093f79dc1d879e6fabdcc89c6c11db570 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 24 Apr 2025 21:16:38 +0100 Subject: [PATCH 065/477] ui: replace qt spinner with raylib (#35059) * use raylib spinner * remove qt spinner * use wrapper, render spinner in thread * english * fix? * match Qt font size more closely --- .gitattributes | 1 - common/spinner.py | 52 -------------- selfdrive/ui/.gitignore | 1 - selfdrive/ui/SConscript | 3 +- selfdrive/ui/qt/spinner.cc | 120 -------------------------------- selfdrive/ui/qt/spinner.h | 37 ---------- selfdrive/ui/qt/spinner_larch64 | 3 - selfdrive/ui/spinner | 7 -- system/athena/registration.py | 2 +- system/manager/build.py | 12 ++-- system/ui/spinner.py | 65 ++++++++++++++--- 11 files changed, 64 insertions(+), 239 deletions(-) delete mode 100644 common/spinner.py delete mode 100644 selfdrive/ui/qt/spinner.cc delete mode 100644 selfdrive/ui/qt/spinner.h delete mode 100755 selfdrive/ui/qt/spinner_larch64 delete mode 100755 selfdrive/ui/spinner diff --git a/.gitattributes b/.gitattributes index eda2505d0e..1384edb626 100644 --- a/.gitattributes +++ b/.gitattributes @@ -11,7 +11,6 @@ selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text -selfdrive/ui/qt/spinner_larch64 filter=lfs diff=lfs merge=lfs -text selfdrive/ui/qt/text_larch64 filter=lfs diff=lfs merge=lfs -text third_party/**/*.a filter=lfs diff=lfs merge=lfs -text third_party/**/*.so filter=lfs diff=lfs merge=lfs -text diff --git a/common/spinner.py b/common/spinner.py deleted file mode 100644 index dcf22641c4..0000000000 --- a/common/spinner.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -import subprocess -from openpilot.common.basedir import BASEDIR - - -class Spinner: - def __init__(self): - try: - self.spinner_proc = subprocess.Popen(["./spinner"], - stdin=subprocess.PIPE, - cwd=os.path.join(BASEDIR, "selfdrive", "ui"), - close_fds=True) - except OSError: - self.spinner_proc = None - - def __enter__(self): - return self - - def update(self, spinner_text: str): - if self.spinner_proc is not None: - self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n") - try: - self.spinner_proc.stdin.flush() - except BrokenPipeError: - pass - - def update_progress(self, cur: float, total: float): - self.update(str(round(100 * cur / total))) - - def close(self): - if self.spinner_proc is not None: - self.spinner_proc.kill() - try: - self.spinner_proc.communicate(timeout=2.) - except subprocess.TimeoutExpired: - print("WARNING: failed to kill spinner") - self.spinner_proc = None - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - -if __name__ == "__main__": - import time - with Spinner() as s: - s.update("Spinner text") - time.sleep(5.0) - print("gone") - time.sleep(5.0) diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore index f9724816da..d9176a3fd4 100644 --- a/selfdrive/ui/.gitignore +++ b/selfdrive/ui/.gitignore @@ -4,7 +4,6 @@ moc_* translations/main_test_en.* _text -_spinner ui mui diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 8fa4b55ea7..233addaa6a 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -66,9 +66,8 @@ if GetOption('extras'): qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) - # spinner and text window + # text window qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs) - qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs) # setup and factory resetter qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs) diff --git a/selfdrive/ui/qt/spinner.cc b/selfdrive/ui/qt/spinner.cc deleted file mode 100644 index 2404efa668..0000000000 --- a/selfdrive/ui/qt/spinner.cc +++ /dev/null @@ -1,120 +0,0 @@ -#include "selfdrive/ui/qt/spinner.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/util.h" - -TrackWidget::TrackWidget(QWidget *parent) : QWidget(parent) { - setAttribute(Qt::WA_OpaquePaintEvent); - setFixedSize(spinner_size); - - // pre-compute all the track imgs. make this a gif instead? - QPixmap comma_img = loadPixmap("../assets/img_spinner_comma.png", spinner_size); - QPixmap track_img = loadPixmap("../assets/img_spinner_track.png", spinner_size); - - QTransform transform(1, 0, 0, 1, width() / 2, height() / 2); - QPixmap pm(spinner_size); - QPainter p(&pm); - p.setRenderHint(QPainter::SmoothPixmapTransform); - for (int i = 0; i < track_imgs.size(); ++i) { - p.resetTransform(); - p.fillRect(0, 0, spinner_size.width(), spinner_size.height(), Qt::black); - p.drawPixmap(0, 0, comma_img); - p.setTransform(transform.rotate(360 / spinner_fps)); - p.drawPixmap(-width() / 2, -height() / 2, track_img); - track_imgs[i] = pm.copy(); - } - - m_anim.setDuration(1000); - m_anim.setStartValue(0); - m_anim.setEndValue(int(track_imgs.size() -1)); - m_anim.setLoopCount(-1); - m_anim.start(); - connect(&m_anim, SIGNAL(valueChanged(QVariant)), SLOT(update())); -} - -void TrackWidget::paintEvent(QPaintEvent *event) { - QPainter painter(this); - painter.drawPixmap(0, 0, track_imgs[m_anim.currentValue().toInt()]); -} - -// Spinner - -Spinner::Spinner(QWidget *parent) : QWidget(parent) { - QGridLayout *main_layout = new QGridLayout(this); - main_layout->setSpacing(0); - main_layout->setMargin(200); - - main_layout->addWidget(new TrackWidget(this), 0, 0, Qt::AlignHCenter | Qt::AlignVCenter); - - text = new QLabel(); - text->setWordWrap(true); - text->setVisible(false); - text->setAlignment(Qt::AlignCenter); - main_layout->addWidget(text, 1, 0, Qt::AlignHCenter); - - progress_bar = new QProgressBar(); - progress_bar->setRange(5, 100); - progress_bar->setTextVisible(false); - progress_bar->setVisible(false); - progress_bar->setFixedHeight(20); - main_layout->addWidget(progress_bar, 1, 0, Qt::AlignHCenter); - - setStyleSheet(R"( - Spinner { - background-color: black; - } - QLabel { - color: white; - font-size: 80px; - background-color: transparent; - } - QProgressBar { - background-color: #373737; - width: 1000px; - border solid white; - border-radius: 10px; - } - QProgressBar::chunk { - border-radius: 10px; - background-color: white; - } - )"); - - notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read); - QObject::connect(notifier, &QSocketNotifier::activated, this, &Spinner::update); -} - -void Spinner::update(int n) { - std::string line; - std::getline(std::cin, line); - - if (line.length()) { - bool number = std::all_of(line.begin(), line.end(), ::isdigit); - text->setVisible(!number); - progress_bar->setVisible(number); - text->setText(QString::fromStdString(line)); - if (number) { - progress_bar->setValue(std::stoi(line)); - } - } -} - -int main(int argc, char *argv[]) { - initApp(argc, argv); - QApplication a(argc, argv); - Spinner spinner; - setMainWindow(&spinner); - return a.exec(); -} diff --git a/selfdrive/ui/qt/spinner.h b/selfdrive/ui/qt/spinner.h deleted file mode 100644 index 43d90a75b0..0000000000 --- a/selfdrive/ui/qt/spinner.h +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -constexpr int spinner_fps = 30; -constexpr QSize spinner_size = QSize(360, 360); - -class TrackWidget : public QWidget { - Q_OBJECT -public: - TrackWidget(QWidget *parent = nullptr); - -private: - void paintEvent(QPaintEvent *event) override; - std::array track_imgs; - QVariantAnimation m_anim; -}; - -class Spinner : public QWidget { - Q_OBJECT - -public: - explicit Spinner(QWidget *parent = 0); - -private: - QLabel *text; - QProgressBar *progress_bar; - QSocketNotifier *notifier; - -public slots: - void update(int n); -}; diff --git a/selfdrive/ui/qt/spinner_larch64 b/selfdrive/ui/qt/spinner_larch64 deleted file mode 100755 index 74081a6925..0000000000 --- a/selfdrive/ui/qt/spinner_larch64 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81d7073d16e8ddc40d4d81fc88f8fc11c434df241593b455e2787935371383e4 -size 3821728 diff --git a/selfdrive/ui/spinner b/selfdrive/ui/spinner deleted file mode 100755 index 965c8f581a..0000000000 --- a/selfdrive/ui/spinner +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -if [ -f /TICI ] && [ ! -f _spinner ]; then - cp qt/spinner_larch64 _spinner -fi - -exec ./_spinner "$1" diff --git a/system/athena/registration.py b/system/athena/registration.py index 964fbff51e..e48ae64aea 100755 --- a/system/athena/registration.py +++ b/system/athena/registration.py @@ -7,10 +7,10 @@ from pathlib import Path from datetime import datetime, timedelta, UTC from openpilot.common.api import api_get from openpilot.common.params import Params -from openpilot.common.spinner import Spinner from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.system.hardware import HARDWARE, PC from openpilot.system.hardware.hw import Paths +from openpilot.system.ui.spinner import Spinner from openpilot.common.swaglog import cloudlog diff --git a/system/manager/build.py b/system/manager/build.py index 93c0546c85..21eff2fecc 100755 --- a/system/manager/build.py +++ b/system/manager/build.py @@ -5,10 +5,10 @@ from pathlib import Path # NOTE: Do NOT import anything here that needs be built (e.g. params) from openpilot.common.basedir import BASEDIR -from openpilot.common.spinner import Spinner from openpilot.common.text_window import TextWindow -from openpilot.system.hardware import HARDWARE, AGNOS from openpilot.common.swaglog import cloudlog, add_file_handler +from openpilot.system.hardware import HARDWARE, AGNOS +from openpilot.system.ui.spinner import Spinner from openpilot.system.version import get_build_metadata MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 @@ -88,7 +88,7 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: if __name__ == "__main__": - spinner = Spinner() - spinner.update_progress(0, 100) - build_metadata = get_build_metadata() - build(spinner, build_metadata.openpilot.is_dirty, minimal = AGNOS) + with Spinner() as spinner: + spinner.update_progress(0, 100) + build_metadata = get_build_metadata() + build(spinner, build_metadata.openpilot.is_dirty, minimal = AGNOS) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 9c1882cf5f..5da96c73f9 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -2,6 +2,7 @@ import pyray as rl import os import threading +import time from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import gui_app @@ -13,8 +14,8 @@ PROGRESS_BAR_HEIGHT = 20 DEGREES_PER_SECOND = 360.0 # one full rotation per second MARGIN_H = 100 TEXTURE_SIZE = 360 -FONT_SIZE = 88 -LINE_HEIGHT = 96 +FONT_SIZE = 96 +LINE_HEIGHT = 104 DARKGRAY = (55, 55, 55, 255) @@ -22,7 +23,7 @@ def clamp(value, min_value, max_value): return max(min(value, max_value), min_value) -class Spinner: +class SpinnerRenderer: def __init__(self): self._comma_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_comma.png"), TEXTURE_SIZE, TEXTURE_SIZE) self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE, @@ -70,7 +71,7 @@ class Spinner: spinner_origin, self._rotation, rl.WHITE) rl.draw_texture_v(self._comma_texture, comma_position, rl.WHITE) - # Display progress bar or text based on user input + # Display the progress bar or text based on user input if progress is not None: bar = rl.Rectangle(center.x - PROGRESS_BAR_WIDTH / 2.0, y_pos, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT) rl.draw_rectangle_rounded(bar, 1, 10, DARKGRAY) @@ -84,9 +85,55 @@ class Spinner: FONT_SIZE, 0.0, rl.WHITE) +class Spinner: + def __init__(self): + self._renderer: SpinnerRenderer | None = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run) + self._thread.start() + + # wait for the renderer to be initialized + while self._renderer is None and self._thread.is_alive(): + time.sleep(0.01) + + def update(self, spinner_text: str): + if self._renderer is not None: + self._renderer.set_text(spinner_text) + + def update_progress(self, cur: float, total: float): + self.update(str(round(100 * cur / total))) + + def _run(self): + if os.getenv("CI") is not None: + return + gui_app.init_window("Spinner") + self._renderer = renderer = SpinnerRenderer() + try: + for _ in gui_app.render(): + if self._stop_event.is_set(): + break + renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def close(self): + if self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + print("WARNING: failed to join spinner thread") + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + if __name__ == "__main__": - gui_app.init_window("Spinner") - spinner = Spinner() - spinner.set_text("Spinner text") - for _ in gui_app.render(): - spinner.render() + with Spinner() as s: + s.update("Spinner text") + time.sleep(5) From 756aebd39f378a6a1db2826b4044290b391cc03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Thu, 24 Apr 2025 16:24:21 -0700 Subject: [PATCH 066/477] Tomb Raider 4 (#35058) * dc455ed7-0ef1-4c57-9391-c71767784c7e/400 * f16884e6-417e-4842-aeda-b2e1b3edf40b/400 --- selfdrive/modeld/modeld.py | 2 +- selfdrive/modeld/models/driving_policy.onnx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index b89c8bad57..9805506432 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -41,7 +41,7 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' -LAT_SMOOTH_SECONDS = 0.1 +LAT_SMOOTH_SECONDS = 0.3 LONG_SMOOTH_SECONDS = 0.3 MIN_LAT_CONTROL_SPEED = 0.3 diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index df11e590dd..528dd298d4 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84061882148485dabfdd23ee7e0925284a4370d0ae5be804d6099ae847352891 +oid sha256:fe85c824c5c41580eb3818400433377b0bfc405927f8cb7d88af9a836bed693d size 15578328 From 37e86df41e2a411ac5db791a8315c29f543e5155 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 24 Apr 2025 18:16:18 -0700 Subject: [PATCH 067/477] CI: comment reporter links on PRs (#35066) * comment * Revert "Tomb Raider 4 (#35058)" This reverts commit 756aebd39f378a6a1db2826b4044290b391cc03c. * no dpeth * Reapply "Tomb Raider 4 (#35058)" This reverts commit 7143835b3d075271e86fb181e08f32db7720c6f8. * no forks --- .github/workflows/model_review.yaml | 42 +++++++++++++++++++++++++++++ scripts/reporter.py | 31 +++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .github/workflows/model_review.yaml create mode 100755 scripts/reporter.py diff --git a/.github/workflows/model_review.yaml b/.github/workflows/model_review.yaml new file mode 100644 index 0000000000..0e1825864c --- /dev/null +++ b/.github/workflows/model_review.yaml @@ -0,0 +1,42 @@ +name: "model review" + +on: + pull_request: + types: [opened, reopened, synchronize] + paths: + - 'selfdrive/modeld/models/*.onnx' + workflow_dispatch: + +jobs: + comment: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + if: github.repository == 'commaai/openpilot' + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Checkout master + uses: actions/checkout@v4 + with: + ref: master + path: base + - run: git lfs pull + - run: cd base && git lfs pull + + - run: pip install onnx + + - name: scripts/reporter.py + id: report + run: | + echo "content<> $GITHUB_OUTPUT + echo "## Model Review" >> $GITHUB_OUTPUT + MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Post model report comment + uses: marocchino/sticky-pull-request-comment@baa7203ed60924babbe5dcd0ac8eae3b66ec5e16 + with: + header: model-review + message: ${{ steps.report.outputs.content }} \ No newline at end of file diff --git a/scripts/reporter.py b/scripts/reporter.py new file mode 100755 index 0000000000..903fcc8911 --- /dev/null +++ b/scripts/reporter.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +import os +import glob +import onnx + +BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) +MASTER_PATH = os.getenv("MASTER_PATH", BASEDIR) +MODEL_PATH = "/selfdrive/modeld/models/" + +def get_checkpoint(f): + model = onnx.load(f) + metadata = {prop.key: prop.value for prop in model.metadata_props} + return metadata['model_checkpoint'].split('/')[0] + +if __name__ == "__main__": + print("| | master | PR branch |") + print("|-| ----- | --------- |") + + for f in glob.glob(BASEDIR + MODEL_PATH + "/*.onnx"): + # TODO: add checkpoint to DM + if "dmonitoring" in f: + continue + + fn = os.path.basename(f) + master = get_checkpoint(MASTER_PATH + MODEL_PATH + fn) + pr = get_checkpoint(BASEDIR + MODEL_PATH + fn) + print( + "|", fn, "|", + f"[{master}](https://reporter.comma.life/experiment/{master})", "|", + f"[{pr}](https://reporter.comma.life/experiment/{pr})", "|" + ) From c961fb095fd192e1b4c325912d0fdb5ee9a7fc26 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 25 Apr 2025 13:57:04 +0100 Subject: [PATCH 068/477] ui(raylib): preserve whitespace in wrapped text (#35067) * ui(raylib): preserve whitespace in wrapped text * lint --- system/ui/text.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/system/ui/text.py b/system/ui/text.py index 1337887d9b..53ea2333ce 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -27,8 +27,10 @@ def wrap_text(text, font_size, max_width): continue indent = re.match(r"^\s*", paragraph).group() current_line = indent - for word in paragraph.split(): - test_line = current_line + word + " " + words = re.split(r"(\s+)", paragraph[len(indent):]) + while len(words): + word = words.pop(0) + test_line = current_line + word + (words.pop(0) if words else "") if rl.measure_text_ex(font, test_line, font_size, 0).x <= max_width: current_line = test_line else: From 92ada2170f51ee20a34d6c4e6cbd746556cfd787 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 25 Apr 2025 14:00:31 +0100 Subject: [PATCH 069/477] ui(raylib): start scroll at bottom of text window (#35068) --- system/ui/text.py | 1 + 1 file changed, 1 insertion(+) diff --git a/system/ui/text.py b/system/ui/text.py index 53ea2333ce..d2e7a1e433 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -48,6 +48,7 @@ class TextWindow: self._wrapped_lines = wrap_text(text, FONT_SIZE, self._textarea_rect.width - 20) self._content_rect = rl.Rectangle(0, 0, self._textarea_rect.width - 20, len(self._wrapped_lines) * LINE_HEIGHT) self._scroll_panel = GuiScrollPanel(show_vertical_scroll_bar=True) + self._scroll_panel._offset.y = -max(self._content_rect.height - self._textarea_rect.height, 0) def render(self): scroll = self._scroll_panel.handle_scroll(self._textarea_rect, self._content_rect) From 7eb1c31d72f95142162d91ff738fe12db150faff Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 25 Apr 2025 14:06:37 +0100 Subject: [PATCH 070/477] ui(raylib/text): spacing between button and scrollbar (#35069) --- system/ui/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/text.py b/system/ui/text.py index d2e7a1e433..531ed1919c 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -7,7 +7,7 @@ from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app MARGIN = 50 -SPACING = 50 +SPACING = 40 FONT_SIZE = 72 LINE_HEIGHT = 80 BUTTON_SIZE = rl.Vector2(310, 160) @@ -60,7 +60,7 @@ class TextWindow: rl.draw_text_ex(gui_app.font(), line, position, FONT_SIZE, 0, rl.WHITE) rl.end_scissor_mode() - button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) + button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x - SPACING, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) ret = gui_button(button_bounds, "Exit" if PC else "Reboot", button_style=ButtonStyle.TRANSPARENT) if ret: if PC: From 362ddfc0c718a2c24e7228583caccf2ec82e5eb0 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 25 Apr 2025 14:17:47 +0100 Subject: [PATCH 071/477] ui: replace qt text window with raylib (#35064) * remove qt text window * use wrapper, render text window in thread * add wait_for_exit method * update imports --- .gitattributes | 1 - common/text_window.py | 63 ----------------------------------- selfdrive/ui/.gitignore | 2 -- selfdrive/ui/SConscript | 3 -- selfdrive/ui/qt/text.cc | 64 ------------------------------------ selfdrive/ui/qt/text_larch64 | 3 -- selfdrive/ui/text | 7 ---- system/manager/build.py | 2 +- system/manager/manager.py | 2 +- system/ui/text.py | 60 ++++++++++++++++++++++++++++----- 10 files changed, 54 insertions(+), 153 deletions(-) delete mode 100755 common/text_window.py delete mode 100644 selfdrive/ui/qt/text.cc delete mode 100755 selfdrive/ui/qt/text_larch64 delete mode 100755 selfdrive/ui/text diff --git a/.gitattributes b/.gitattributes index 1384edb626..cc1605a132 100644 --- a/.gitattributes +++ b/.gitattributes @@ -11,7 +11,6 @@ selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text -selfdrive/ui/qt/text_larch64 filter=lfs diff=lfs merge=lfs -text third_party/**/*.a filter=lfs diff=lfs merge=lfs -text third_party/**/*.so filter=lfs diff=lfs merge=lfs -text third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text diff --git a/common/text_window.py b/common/text_window.py deleted file mode 100755 index d2762ebf7d..0000000000 --- a/common/text_window.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -import os -import time -import subprocess -from openpilot.common.basedir import BASEDIR - - -class TextWindow: - def __init__(self, text): - try: - self.text_proc = subprocess.Popen(["./text", text], - stdin=subprocess.PIPE, - cwd=os.path.join(BASEDIR, "selfdrive", "ui"), - close_fds=True) - except OSError: - self.text_proc = None - - def get_status(self): - if self.text_proc is not None: - self.text_proc.poll() - return self.text_proc.returncode - return None - - def __enter__(self): - return self - - def close(self): - if self.text_proc is not None: - self.text_proc.terminate() - self.text_proc = None - - def wait_for_exit(self): - if self.text_proc is not None: - while True: - if self.get_status() == 1: - return - time.sleep(0.1) - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - -if __name__ == "__main__": - text = """Traceback (most recent call last): - File "./controlsd.py", line 608, in - main() - File "./controlsd.py", line 604, in main - controlsd_thread(sm, pm, logcan) - File "./controlsd.py", line 455, in controlsd_thread - 1/0 -ZeroDivisionError: division by zero""" - print(text) - - with TextWindow(text) as s: - for _ in range(100): - if s.get_status() == 1: - print("Got exit button") - break - time.sleep(0.1) - print("gone") diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore index d9176a3fd4..7e9eaf932f 100644 --- a/selfdrive/ui/.gitignore +++ b/selfdrive/ui/.gitignore @@ -3,8 +3,6 @@ moc_* translations/main_test_en.* -_text - ui mui watch3 diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 233addaa6a..e63359da05 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -66,9 +66,6 @@ if GetOption('extras'): qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) - # text window - qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs) - # setup and factory resetter qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs) qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc", asset_obj], diff --git a/selfdrive/ui/qt/text.cc b/selfdrive/ui/qt/text.cc deleted file mode 100644 index 21ec5eedcf..0000000000 --- a/selfdrive/ui/qt/text.cc +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "system/hardware/hw.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/ui/qt/widgets/scrollview.h" - -int main(int argc, char *argv[]) { - initApp(argc, argv); - QApplication a(argc, argv); - QWidget window; - setMainWindow(&window); - - QGridLayout *main_layout = new QGridLayout(&window); - main_layout->setMargin(50); - - QLabel *label = new QLabel(argv[1]); - label->setWordWrap(true); - label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - ScrollView *scroll = new ScrollView(label); - scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - main_layout->addWidget(scroll, 0, 0, Qt::AlignTop); - - // Scroll to the bottom - QObject::connect(scroll->verticalScrollBar(), &QAbstractSlider::rangeChanged, [=]() { - scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum()); - }); - - QPushButton *btn = new QPushButton(); -#ifdef __aarch64__ - btn->setText(QObject::tr("Reboot")); - QObject::connect(btn, &QPushButton::clicked, [=]() { - Hardware::reboot(); - }); -#else - btn->setText(QObject::tr("Exit")); - QObject::connect(btn, &QPushButton::clicked, &a, &QApplication::quit); -#endif - main_layout->addWidget(btn, 0, 0, Qt::AlignRight | Qt::AlignBottom); - - window.setStyleSheet(R"( - * { - outline: none; - color: white; - background-color: black; - font-size: 60px; - } - QPushButton { - padding: 50px; - padding-right: 100px; - padding-left: 100px; - border: 2px solid white; - border-radius: 20px; - margin-right: 40px; - } - )"); - - return a.exec(); -} diff --git a/selfdrive/ui/qt/text_larch64 b/selfdrive/ui/qt/text_larch64 deleted file mode 100755 index 7d74f4d31c..0000000000 --- a/selfdrive/ui/qt/text_larch64 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61f539845ebfc9568c8d28867f1e5642e882f52ead8862c9b2224b7139f4a552 -size 3787480 diff --git a/selfdrive/ui/text b/selfdrive/ui/text deleted file mode 100755 index b12235f4e6..0000000000 --- a/selfdrive/ui/text +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -if [ -f /TICI ] && [ ! -f _text ]; then - cp qt/text_larch64 _text -fi - -exec ./_text "$1" diff --git a/system/manager/build.py b/system/manager/build.py index 21eff2fecc..771024794f 100755 --- a/system/manager/build.py +++ b/system/manager/build.py @@ -5,10 +5,10 @@ from pathlib import Path # NOTE: Do NOT import anything here that needs be built (e.g. params) from openpilot.common.basedir import BASEDIR -from openpilot.common.text_window import TextWindow from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.system.hardware import HARDWARE, AGNOS from openpilot.system.ui.spinner import Spinner +from openpilot.system.ui.text import TextWindow from openpilot.system.version import get_build_metadata MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 diff --git a/system/manager/manager.py b/system/manager/manager.py index 89e5a472f2..d8c56cbc73 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -9,7 +9,6 @@ from cereal import log import cereal.messaging as messaging import openpilot.system.sentry as sentry from openpilot.common.params import Params, ParamKeyType -from openpilot.common.text_window import TextWindow from openpilot.system.hardware import HARDWARE from openpilot.system.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog from openpilot.system.manager.process import ensure_running @@ -18,6 +17,7 @@ from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_I from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.system.version import get_build_metadata, terms_version, training_version from openpilot.system.hardware.hw import Paths +from openpilot.system.ui.text import TextWindow def manager_init() -> None: diff --git a/system/ui/text.py b/system/ui/text.py index 531ed1919c..e57ae45d9a 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,5 +1,8 @@ #!/usr/bin/env python3 +import os import re +import threading +import time import pyray as rl from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.button import gui_button, ButtonStyle @@ -42,7 +45,8 @@ def wrap_text(text, font_size, max_width): return lines -class TextWindow: + +class TextWindowRenderer: def __init__(self, text: str): self._textarea_rect = rl.Rectangle(MARGIN, MARGIN, gui_app.width - MARGIN * 2, gui_app.height - MARGIN * 2) self._wrapped_lines = wrap_text(text, FONT_SIZE, self._textarea_rect.width - 20) @@ -70,13 +74,53 @@ class TextWindow: return ret -def show_text_in_window(text: str): - gui_app.init_window("Text") - text_window = TextWindow(text) - for _ in gui_app.render(): - text_window.render() - gui_app.close() +class TextWindow: + def __init__(self, text: str): + self._text = text + + self._renderer: TextWindowRenderer | None = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run) + self._thread.start() + + # wait for the renderer to be initialized + while self._renderer is None and self._thread.is_alive(): + time.sleep(0.01) + + def wait_for_exit(self): + while self._thread.is_alive(): + time.sleep(0.01) + + def _run(self): + if os.getenv("CI") is not None: + return + gui_app.init_window("Text") + self._renderer = renderer = TextWindowRenderer(self._text) + try: + for _ in gui_app.render(): + if self._stop_event.is_set(): + break + renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def close(self): + if self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + print("WARNING: failed to join text window thread") + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() if __name__ == "__main__": - show_text_in_window(DEMO_TEXT) + with TextWindow(DEMO_TEXT): + time.sleep(5) From 37839ea0c59dd80b2129d7594e5b9aff99fd1d9b Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Fri, 25 Apr 2025 16:57:18 -0400 Subject: [PATCH 072/477] controls: publish current curvature in CarControl (#34679) * controls: publish current curvature in CarControl * clean up * update refs --------- Co-authored-by: Shane Smiskol --- selfdrive/controls/controlsd.py | 1 + selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index dfff26d9db..5ecff1ccdf 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -141,6 +141,7 @@ class Controls: # Orientation and angle rates can be useful for carcontroller # Only calibrated (car) frame is relevant for the carcontroller + CC.currentCurvature = self.curvature if self.calibrated_pose is not None: CC.orientationNED = self.calibrated_pose.orientation.xyz.tolist() CC.angularVelocity = self.calibrated_pose.angular_velocity.xyz.tolist() diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 4b3ef33407..5f8610d58f 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -fcc771b9ceb487a61035885acbd84e592fb316bf \ No newline at end of file +4c7ac1a362933af9d97697979162087c1ab2584b \ No newline at end of file From 38610419dc31b967f6d5f9633876287a5f275714 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:58:25 -0700 Subject: [PATCH 073/477] [bot] Update Python packages (#35041) Update Python packages Co-authored-by: Vehicle Researcher --- docs/CARS.md | 137 +-- opendbc_repo | 2 +- panda | 2 +- uv.lock | 3267 +++++++++++++++++++++++++------------------------- 4 files changed, 1708 insertions(+), 1700 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index a1b027c51b..95af300c0f 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -11,12 +11,12 @@ A supported vehicle is one that just works when you install a comma device. All |Acura|ILX 2016-19|AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -28,7 +28,7 @@ A supported vehicle is one that just works when you install a comma device. All |Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Chrysler|Pacifica Hybrid 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| -|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -195,8 +195,8 @@ A supported vehicle is one that just works when you install a comma device. All |Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Nissan[7](#footnotes)|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -206,8 +206,8 @@ A supported vehicle is one that just works when you install a comma device. All |Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| @@ -218,19 +218,19 @@ A supported vehicle is one that just works when you install a comma device. All |Subaru|Outback 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|XV 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| |Subaru|XV 2020-21|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Škoda|Fabia 2022-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| -|Škoda|Kamiq 2021-23[12,14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| -|Škoda|Karoq 2019-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Kodiaq 2017-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia 2015-19[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia RS 2016[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia Scout 2017-19[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Scala 2020-23[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| -|Škoda|Superb 2015-22[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla|Model Y (with HW4) 2024[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Fabia 2022-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| +|Škoda|Kamiq 2021-23[13,15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| +|Škoda|Karoq 2019-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Kodiaq 2017-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia 2015-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia RS 2016[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia Scout 2017-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Scala 2020-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| +|Škoda|Superb 2015-22[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla[11](#footnotes)|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla[11](#footnotes)|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla[11](#footnotes)|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Tesla[11](#footnotes)|Model Y (with HW4) 2024[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -243,8 +243,8 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|C-HR 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|C-HR Hybrid 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2018-20|All|Stock|0 mph[11](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2021-24|All|openpilot|0 mph[11](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2018-20|All|Stock|0 mph[12](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2021-24|All|openpilot|0 mph[12](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -276,42 +276,42 @@ A supported vehicle is one that just works when you install a comma device. All |Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|RAV4 Hybrid 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat 2015-22[13](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| -|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[16](#footnotes)|| -|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,15](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat 2015-22[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| +|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| +|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| ### Footnotes 1openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`.
@@ -324,12 +324,13 @@ A supported vehicle is one that just works when you install a comma device. All 8In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.
9Enabling longitudinal control (alpha) will disable all EyeSight functionality, including AEB, LDW, and RAB.
10Some 2023 model years have HW4. To check which hardware type your vehicle has, look for Autopilot computer under Software -> Additional Vehicle Information on your vehicle's touchscreen. See this page for more information.
-11openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
-12Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-13Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
-14Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality.
-15Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
-16Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
+11See more setup details for Tesla.
+12openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
+13Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
+14Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
+15Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma 3X functionality.
+16Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
+17Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
## Community Maintained Cars Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/). diff --git a/opendbc_repo b/opendbc_repo index 3b33a71a2b..7bad67af29 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 3b33a71a2bf08b57dd5b2bf77e26cc376dcd7cc9 +Subproject commit 7bad67af29ea117ce8cf51cc6549c97b9acdafe1 diff --git a/panda b/panda index d319ea2bba..b4773f96b3 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit d319ea2bbae4ee0f5dea12d7ca5456cd34288d55 +Subproject commit b4773f96b38a56089b28bf70b8073a9ddce6d847 diff --git a/uv.lock b/uv.lock index 6911f053cb..a8a99e9979 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.11, <3.13" resolution-markers = [ "python_full_version >= '3.12' and sys_platform == 'darwin'", @@ -14,14 +14,14 @@ resolution-markers = [ name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload_time = "2025-03-12T01:42:48.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload_time = "2025-03-12T01:42:47.083Z" }, ] [[package]] name = "aiohttp" -version = "3.11.16" +version = "3.11.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -32,40 +32,40 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/d9/1c4721d143e14af753f2bf5e3b681883e1f24b592c0482df6fa6e33597fa/aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8", size = 7676826 } +sdist = { url = "https://files.pythonhosted.org/packages/63/e7/fa1a8c00e2c54b05dc8cb5d1439f627f7c267874e3f7bb047146116020f9/aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a", size = 7678653, upload_time = "2025-04-21T09:43:09.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/98/be30539cd84260d9f3ea1936d50445e25aa6029a4cb9707f3b64cfd710f7/aiohttp-3.11.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cb0688a8d81c63d716e867d59a9ccc389e97ac7037ebef904c2b89334407180", size = 708664 }, - { url = "https://files.pythonhosted.org/packages/e6/27/d51116ce18bdfdea7a2244b55ad38d7b01a4298af55765eed7e8431f013d/aiohttp-3.11.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad1fb47da60ae1ddfb316f0ff16d1f3b8e844d1a1e154641928ea0583d486ed", size = 468953 }, - { url = "https://files.pythonhosted.org/packages/34/23/eedf80ec42865ea5355b46265a2433134138eff9a4fea17e1348530fa4ae/aiohttp-3.11.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df7db76400bf46ec6a0a73192b14c8295bdb9812053f4fe53f4e789f3ea66bbb", size = 456065 }, - { url = "https://files.pythonhosted.org/packages/36/23/4a5b1ef6cff994936bf96d981dd817b487d9db755457a0d1c2939920d620/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc3a145479a76ad0ed646434d09216d33d08eef0d8c9a11f5ae5cdc37caa3540", size = 1687976 }, - { url = "https://files.pythonhosted.org/packages/d0/5d/c7474b4c3069bb35276d54c82997dff4f7575e4b73f0a7b1b08a39ece1eb/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d007aa39a52d62373bd23428ba4a2546eed0e7643d7bf2e41ddcefd54519842c", size = 1752711 }, - { url = "https://files.pythonhosted.org/packages/64/4c/ee416987b6729558f2eb1b727c60196580aafdb141e83bd78bb031d1c000/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6ddd90d9fb4b501c97a4458f1c1720e42432c26cb76d28177c5b5ad4e332601", size = 1791305 }, - { url = "https://files.pythonhosted.org/packages/58/28/3e1e1884070b95f1f69c473a1995852a6f8516670bb1c29d6cb2dbb73e1c/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a2f451849e6b39e5c226803dcacfa9c7133e9825dcefd2f4e837a2ec5a3bb98", size = 1674499 }, - { url = "https://files.pythonhosted.org/packages/ad/55/a032b32fa80a662d25d9eb170ed1e2c2be239304ca114ec66c89dc40f37f/aiohttp-3.11.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8df6612df74409080575dca38a5237282865408016e65636a76a2eb9348c2567", size = 1622313 }, - { url = "https://files.pythonhosted.org/packages/b1/df/ca775605f72abbda4e4746e793c408c84373ca2c6ce7a106a09f853f1e89/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78e6e23b954644737e385befa0deb20233e2dfddf95dd11e9db752bdd2a294d3", size = 1658274 }, - { url = "https://files.pythonhosted.org/packages/cc/6c/21c45b66124df5b4b0ab638271ecd8c6402b702977120cb4d5be6408e15d/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:696ef00e8a1f0cec5e30640e64eca75d8e777933d1438f4facc9c0cdf288a810", size = 1666704 }, - { url = "https://files.pythonhosted.org/packages/1d/e2/7d92adc03e3458edd18a21da2575ab84e58f16b1672ae98529e4eeee45ab/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3538bc9fe1b902bef51372462e3d7c96fce2b566642512138a480b7adc9d508", size = 1652815 }, - { url = "https://files.pythonhosted.org/packages/3a/52/7549573cd654ad651e3c5786ec3946d8f0ee379023e22deb503ff856b16c/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ab3367bb7f61ad18793fea2ef71f2d181c528c87948638366bf1de26e239183", size = 1735669 }, - { url = "https://files.pythonhosted.org/packages/d5/54/dcd24a23c7a5a2922123e07a296a5f79ea87ce605f531be068415c326de6/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:56a3443aca82abda0e07be2e1ecb76a050714faf2be84256dae291182ba59049", size = 1760422 }, - { url = "https://files.pythonhosted.org/packages/a7/53/87327fe982fa310944e1450e97bf7b2a28015263771931372a1dfe682c58/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61c721764e41af907c9d16b6daa05a458f066015abd35923051be8705108ed17", size = 1694457 }, - { url = "https://files.pythonhosted.org/packages/ce/6d/c5ccf41059267bcf89853d3db9d8d217dacf0a04f4086cb6bf278323011f/aiohttp-3.11.16-cp311-cp311-win32.whl", hash = "sha256:3e061b09f6fa42997cf627307f220315e313ece74907d35776ec4373ed718b86", size = 416817 }, - { url = "https://files.pythonhosted.org/packages/e7/dd/01f6fe028e054ef4f909c9d63e3a2399e77021bb2e1bb51d56ca8b543989/aiohttp-3.11.16-cp311-cp311-win_amd64.whl", hash = "sha256:745f1ed5e2c687baefc3c5e7b4304e91bf3e2f32834d07baaee243e349624b24", size = 442986 }, - { url = "https://files.pythonhosted.org/packages/db/38/100d01cbc60553743baf0fba658cb125f8ad674a8a771f765cdc155a890d/aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27", size = 704881 }, - { url = "https://files.pythonhosted.org/packages/21/ed/b4102bb6245e36591209e29f03fe87e7956e54cb604ee12e20f7eb47f994/aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713", size = 464564 }, - { url = "https://files.pythonhosted.org/packages/3b/e1/a9ab6c47b62ecee080eeb33acd5352b40ecad08fb2d0779bcc6739271745/aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb", size = 456548 }, - { url = "https://files.pythonhosted.org/packages/80/ad/216c6f71bdff2becce6c8776f0aa32cb0fa5d83008d13b49c3208d2e4016/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321", size = 1691749 }, - { url = "https://files.pythonhosted.org/packages/bd/ea/7df7bcd3f4e734301605f686ffc87993f2d51b7acb6bcc9b980af223f297/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e", size = 1736874 }, - { url = "https://files.pythonhosted.org/packages/51/41/c7724b9c87a29b7cfd1202ec6446bae8524a751473d25e2ff438bc9a02bf/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c", size = 1786885 }, - { url = "https://files.pythonhosted.org/packages/86/b3/f61f8492fa6569fa87927ad35a40c159408862f7e8e70deaaead349e2fba/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce", size = 1698059 }, - { url = "https://files.pythonhosted.org/packages/ce/be/7097cf860a9ce8bbb0e8960704e12869e111abcd3fbd245153373079ccec/aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e", size = 1626527 }, - { url = "https://files.pythonhosted.org/packages/1d/1d/aaa841c340e8c143a8d53a1f644c2a2961c58cfa26e7b398d6bf75cf5d23/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b", size = 1644036 }, - { url = "https://files.pythonhosted.org/packages/2c/88/59d870f76e9345e2b149f158074e78db457985c2b4da713038d9da3020a8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540", size = 1685270 }, - { url = "https://files.pythonhosted.org/packages/2b/b1/c6686948d4c79c3745595efc469a9f8a43cab3c7efc0b5991be65d9e8cb8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b", size = 1650852 }, - { url = "https://files.pythonhosted.org/packages/fe/94/3e42a6916fd3441721941e0f1b8438e1ce2a4c49af0e28e0d3c950c9b3c9/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e", size = 1704481 }, - { url = "https://files.pythonhosted.org/packages/b1/6d/6ab5854ff59b27075c7a8c610597d2b6c38945f9a1284ee8758bc3720ff6/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c", size = 1735370 }, - { url = "https://files.pythonhosted.org/packages/73/2a/08a68eec3c99a6659067d271d7553e4d490a0828d588e1daa3970dc2b771/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71", size = 1697619 }, - { url = "https://files.pythonhosted.org/packages/61/d5/fea8dbbfb0cd68fbb56f0ae913270a79422d9a41da442a624febf72d2aaf/aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2", size = 411710 }, - { url = "https://files.pythonhosted.org/packages/33/fb/41cde15fbe51365024550bf77b95a4fc84ef41365705c946da0421f0e1e0/aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682", size = 438012 }, + { url = "https://files.pythonhosted.org/packages/2f/10/fd9ee4f9e042818c3c2390054c08ccd34556a3cb209d83285616434cf93e/aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9", size = 712088, upload_time = "2025-04-21T09:40:55.776Z" }, + { url = "https://files.pythonhosted.org/packages/22/eb/6a77f055ca56f7aae2cd2a5607a3c9e7b9554f1497a069dcfcb52bfc9540/aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b", size = 471450, upload_time = "2025-04-21T09:40:57.301Z" }, + { url = "https://files.pythonhosted.org/packages/78/dc/5f3c0d27c91abf0bb5d103e9c9b0ff059f60cf6031a5f06f456c90731f42/aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66", size = 457836, upload_time = "2025-04-21T09:40:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/49/7b/55b65af9ef48b9b811c91ff8b5b9de9650c71147f10523e278d297750bc8/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756", size = 1690978, upload_time = "2025-04-21T09:41:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5a/3f8938c4f68ae400152b42742653477fc625d6bfe02e764f3521321c8442/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717", size = 1745307, upload_time = "2025-04-21T09:41:02.89Z" }, + { url = "https://files.pythonhosted.org/packages/b4/42/89b694a293333ef6f771c62da022163bcf44fb03d4824372d88e3dc12530/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4", size = 1780692, upload_time = "2025-04-21T09:41:04.461Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ce/1a75384e01dd1bf546898b6062b1b5f7a59b6692ef802e4dd6db64fed264/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f", size = 1676934, upload_time = "2025-04-21T09:41:06.728Z" }, + { url = "https://files.pythonhosted.org/packages/a5/31/442483276e6c368ab5169797d9873b5875213cbcf7e74b95ad1c5003098a/aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361", size = 1621190, upload_time = "2025-04-21T09:41:08.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/83/90274bf12c079457966008a58831a99675265b6a34b505243e004b408934/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1", size = 1658947, upload_time = "2025-04-21T09:41:11.054Z" }, + { url = "https://files.pythonhosted.org/packages/91/c1/da9cee47a0350b78fdc93670ebe7ad74103011d7778ab4c382ca4883098d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421", size = 1654443, upload_time = "2025-04-21T09:41:13.213Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f2/73cbe18dc25d624f79a09448adfc4972f82ed6088759ddcf783cd201956c/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e", size = 1644169, upload_time = "2025-04-21T09:41:14.827Z" }, + { url = "https://files.pythonhosted.org/packages/5b/32/970b0a196c4dccb1b0cfa5b4dc3b20f63d76f1c608f41001a84b2fd23c3d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d", size = 1728532, upload_time = "2025-04-21T09:41:17.168Z" }, + { url = "https://files.pythonhosted.org/packages/0b/50/b1dc810a41918d2ea9574e74125eb053063bc5e14aba2d98966f7d734da0/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f", size = 1750310, upload_time = "2025-04-21T09:41:19.353Z" }, + { url = "https://files.pythonhosted.org/packages/95/24/39271f5990b35ff32179cc95537e92499d3791ae82af7dcf562be785cd15/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd", size = 1691580, upload_time = "2025-04-21T09:41:21.868Z" }, + { url = "https://files.pythonhosted.org/packages/6b/78/75d0353feb77f041460564f12fe58e456436bbc00cbbf5d676dbf0038cc2/aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d", size = 417565, upload_time = "2025-04-21T09:41:24.78Z" }, + { url = "https://files.pythonhosted.org/packages/ed/97/b912dcb654634a813f8518de359364dfc45976f822116e725dc80a688eee/aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6", size = 443652, upload_time = "2025-04-21T09:41:26.48Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d2/5bc436f42bf4745c55f33e1e6a2d69e77075d3e768e3d1a34f96ee5298aa/aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2", size = 706671, upload_time = "2025-04-21T09:41:28.021Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d0/2dbabecc4e078c0474abb40536bbde717fb2e39962f41c5fc7a216b18ea7/aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508", size = 466169, upload_time = "2025-04-21T09:41:29.783Z" }, + { url = "https://files.pythonhosted.org/packages/70/84/19edcf0b22933932faa6e0be0d933a27bd173da02dc125b7354dff4d8da4/aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e", size = 457554, upload_time = "2025-04-21T09:41:31.327Z" }, + { url = "https://files.pythonhosted.org/packages/32/d0/e8d1f034ae5624a0f21e4fb3feff79342ce631f3a4d26bd3e58b31ef033b/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f", size = 1690154, upload_time = "2025-04-21T09:41:33.541Z" }, + { url = "https://files.pythonhosted.org/packages/16/de/2f9dbe2ac6f38f8495562077131888e0d2897e3798a0ff3adda766b04a34/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f", size = 1733402, upload_time = "2025-04-21T09:41:35.634Z" }, + { url = "https://files.pythonhosted.org/packages/e0/04/bd2870e1e9aef990d14b6df2a695f17807baf5c85a4c187a492bda569571/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec", size = 1783958, upload_time = "2025-04-21T09:41:37.456Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/4203ffa2beb5bedb07f0da0f79b7d9039d1c33f522e0d1a2d5b6218e6f2e/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6", size = 1695288, upload_time = "2025-04-21T09:41:39.756Z" }, + { url = "https://files.pythonhosted.org/packages/30/b2/e2285dda065d9f29ab4b23d8bcc81eb881db512afb38a3f5247b191be36c/aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009", size = 1618871, upload_time = "2025-04-21T09:41:41.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/e0/88f2987885d4b646de2036f7296ebea9268fdbf27476da551c1a7c158bc0/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4", size = 1646262, upload_time = "2025-04-21T09:41:44.192Z" }, + { url = "https://files.pythonhosted.org/packages/e0/19/4d2da508b4c587e7472a032290b2981f7caeca82b4354e19ab3df2f51d56/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9", size = 1677431, upload_time = "2025-04-21T09:41:46.049Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/047473ea50150a41440f3265f53db1738870b5a1e5406ece561ca61a3bf4/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb", size = 1637430, upload_time = "2025-04-21T09:41:47.973Z" }, + { url = "https://files.pythonhosted.org/packages/11/32/c6d1e3748077ce7ee13745fae33e5cb1dac3e3b8f8787bf738a93c94a7d2/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda", size = 1703342, upload_time = "2025-04-21T09:41:50.323Z" }, + { url = "https://files.pythonhosted.org/packages/c5/1d/a3b57bfdbe285f0d45572d6d8f534fd58761da3e9cbc3098372565005606/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1", size = 1740600, upload_time = "2025-04-21T09:41:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a5/71/f9cd2fed33fa2b7ce4d412fb7876547abb821d5b5520787d159d0748321d/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea", size = 1695131, upload_time = "2025-04-21T09:41:53.94Z" }, + { url = "https://files.pythonhosted.org/packages/97/97/d1248cd6d02b9de6aa514793d0dcb20099f0ec47ae71a933290116c070c5/aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8", size = 412442, upload_time = "2025-04-21T09:41:55.689Z" }, + { url = "https://files.pythonhosted.org/packages/33/9a/e34e65506e06427b111e19218a99abf627638a9703f4b8bcc3e3021277ed/aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8", size = 439444, upload_time = "2025-04-21T09:41:57.977Z" }, ] [[package]] @@ -76,9 +76,9 @@ dependencies = [ { name = "dnspython" }, { name = "ifaddr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/a2/45dfab1d5a7f96c48595a5770379acf406cdf02a2cd1ac1729b599322b08/aioice-0.10.1.tar.gz", hash = "sha256:5c8e1422103448d171925c678fb39795e5fe13d79108bebb00aa75a899c2094a", size = 44304 } +sdist = { url = "https://files.pythonhosted.org/packages/95/a2/45dfab1d5a7f96c48595a5770379acf406cdf02a2cd1ac1729b599322b08/aioice-0.10.1.tar.gz", hash = "sha256:5c8e1422103448d171925c678fb39795e5fe13d79108bebb00aa75a899c2094a", size = 44304, upload_time = "2025-04-13T08:15:25.629Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/58/af07dda649c22a1ae954ffb7aaaf4d4a57f1bf00ebdf62307affc0b8552f/aioice-0.10.1-py3-none-any.whl", hash = "sha256:f31ae2abc8608b1283ed5f21aebd7b6bd472b152ff9551e9b559b2d8efed79e9", size = 24872 }, + { url = "https://files.pythonhosted.org/packages/3b/58/af07dda649c22a1ae954ffb7aaaf4d4a57f1bf00ebdf62307affc0b8552f/aioice-0.10.1-py3-none-any.whl", hash = "sha256:f31ae2abc8608b1283ed5f21aebd7b6bd472b152ff9551e9b559b2d8efed79e9", size = 24872, upload_time = "2025-04-13T08:15:24.044Z" }, ] [[package]] @@ -95,15 +95,15 @@ dependencies = [ { name = "pylibsrtp" }, { name = "pyopenssl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/f8/408e092748521889c9d33dddcef920afd9891cf6db4615ba6b6bfe114ff8/aiortc-1.10.1.tar.gz", hash = "sha256:64926ad86bde20c1a4dacb7c3a164e57b522606b70febe261fada4acf79641b5", size = 1179406 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/f8/408e092748521889c9d33dddcef920afd9891cf6db4615ba6b6bfe114ff8/aiortc-1.10.1.tar.gz", hash = "sha256:64926ad86bde20c1a4dacb7c3a164e57b522606b70febe261fada4acf79641b5", size = 1179406, upload_time = "2025-02-02T17:36:38.684Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/6b/74547a30d1ddcc81f905ef4ff7fcc2c89b7482cb2045688f2aaa4fa918aa/aiortc-1.10.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3bef536f38394b518aefae9dbf9cdd08f39e4c425f316f9692f0d8dc724810bd", size = 1218457 }, - { url = "https://files.pythonhosted.org/packages/46/92/b4ccf39cd18e366ace2a11dc7d98ed55967b4b325707386b5788149db15e/aiortc-1.10.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8842c02e38513d9432ef22982572833487bb015f23348fa10a690616dbf55143", size = 898855 }, - { url = "https://files.pythonhosted.org/packages/a4/e9/2676de48b493787d8b03129713e6bb2dfbacca2a565090f2a89cbad71f96/aiortc-1.10.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:954a420de01c0bf6b07a0c58b662029b1c4204ddbd8f5c4162bbdebd43f882b1", size = 1750403 }, - { url = "https://files.pythonhosted.org/packages/c3/9d/ab6d09183cdaf5df060923d9bd5c9ed5fb1802661d9401dba35f3c85a57b/aiortc-1.10.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7c0d46fb30307a9d7deb4b7d66f0b0e73b77a7221b063fb6dc78821a5d2aa1e", size = 1867886 }, - { url = "https://files.pythonhosted.org/packages/c2/71/0b5666e6b965dbd9a7f331aa827a6c3ab3eb4d582fefb686a7f4227b7954/aiortc-1.10.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89582f6923046f79f15d9045f432bc78191eacc95f6bed18714e86ec935188d9", size = 1893709 }, - { url = "https://files.pythonhosted.org/packages/9d/0a/8c0c78fad79ef595a0ed6e2ab413900e6bd0eac65fc5c31c9d8736bff909/aiortc-1.10.1-cp39-abi3-win32.whl", hash = "sha256:d1cbe87f740b33ffaa8e905f21092773e74916be338b64b81c8b79af4c3847eb", size = 923265 }, - { url = "https://files.pythonhosted.org/packages/73/12/a27dd588a4988021da88cb4d338d8ee65ac097afc14e9193ab0be4a48790/aiortc-1.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:c9a5a0b23f8a77540068faec8837fa0a65b0396c20f09116bdb874b75e0b6abe", size = 1009488 }, + { url = "https://files.pythonhosted.org/packages/0a/6b/74547a30d1ddcc81f905ef4ff7fcc2c89b7482cb2045688f2aaa4fa918aa/aiortc-1.10.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3bef536f38394b518aefae9dbf9cdd08f39e4c425f316f9692f0d8dc724810bd", size = 1218457, upload_time = "2025-02-02T17:36:23.172Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/b4ccf39cd18e366ace2a11dc7d98ed55967b4b325707386b5788149db15e/aiortc-1.10.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8842c02e38513d9432ef22982572833487bb015f23348fa10a690616dbf55143", size = 898855, upload_time = "2025-02-02T17:36:25.9Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/2676de48b493787d8b03129713e6bb2dfbacca2a565090f2a89cbad71f96/aiortc-1.10.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:954a420de01c0bf6b07a0c58b662029b1c4204ddbd8f5c4162bbdebd43f882b1", size = 1750403, upload_time = "2025-02-02T17:36:28.446Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9d/ab6d09183cdaf5df060923d9bd5c9ed5fb1802661d9401dba35f3c85a57b/aiortc-1.10.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7c0d46fb30307a9d7deb4b7d66f0b0e73b77a7221b063fb6dc78821a5d2aa1e", size = 1867886, upload_time = "2025-02-02T17:36:30.209Z" }, + { url = "https://files.pythonhosted.org/packages/c2/71/0b5666e6b965dbd9a7f331aa827a6c3ab3eb4d582fefb686a7f4227b7954/aiortc-1.10.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89582f6923046f79f15d9045f432bc78191eacc95f6bed18714e86ec935188d9", size = 1893709, upload_time = "2025-02-02T17:36:32.342Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0a/8c0c78fad79ef595a0ed6e2ab413900e6bd0eac65fc5c31c9d8736bff909/aiortc-1.10.1-cp39-abi3-win32.whl", hash = "sha256:d1cbe87f740b33ffaa8e905f21092773e74916be338b64b81c8b79af4c3847eb", size = 923265, upload_time = "2025-02-02T17:36:34.685Z" }, + { url = "https://files.pythonhosted.org/packages/73/12/a27dd588a4988021da88cb4d338d8ee65ac097afc14e9193ab0be4a48790/aiortc-1.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:c9a5a0b23f8a77540068faec8837fa0a65b0396c20f09116bdb874b75e0b6abe", size = 1009488, upload_time = "2025-02-02T17:36:36.317Z" }, ] [[package]] @@ -113,38 +113,38 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424, upload_time = "2024-12-13T17:10:40.86Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload_time = "2024-12-13T17:10:38.469Z" }, ] [[package]] name = "attrs" version = "25.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload_time = "2025-03-13T11:10:22.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload_time = "2025-03-13T11:10:21.14Z" }, ] [[package]] name = "av" version = "13.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/9d/486d31e76784cc0ad943f420c5e05867263b32b37e2f4b0f7f22fdc1ca3a/av-13.1.0.tar.gz", hash = "sha256:d3da736c55847d8596eb8c26c60e036f193001db3bc5c10da8665622d906c17e", size = 3957908 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/9d/486d31e76784cc0ad943f420c5e05867263b32b37e2f4b0f7f22fdc1ca3a/av-13.1.0.tar.gz", hash = "sha256:d3da736c55847d8596eb8c26c60e036f193001db3bc5c10da8665622d906c17e", size = 3957908, upload_time = "2024-10-06T04:54:57.507Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/54/c4227080c9700384db90072ace70d89b6a288b3748bd2ec0e32580a49e7f/av-13.1.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:867385e6701464a5c95903e24d2e0df1c7e0dbf211ed91d0ce639cd687373e10", size = 24255112 }, - { url = "https://files.pythonhosted.org/packages/32/4a/eb9348231655ca99b200b380f4edbceff7358c927a285badcc84b18fb1c9/av-13.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb7a3f319401a46b0017771268ff4928501e77cf00b1a2aa0721e20b2fd1146e", size = 19467930 }, - { url = "https://files.pythonhosted.org/packages/14/c7/48c80252bdbc3a75a54dd205a7fab8f613914009b9e5416202757208e040/av-13.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad904f860147bceaca65b0d3174a8153f35c570d465161d210f1879970b15559", size = 32207671 }, - { url = "https://files.pythonhosted.org/packages/f9/66/3332c7fa8c43b65680a94f279ea3e832b5500de3a1392bac6112881e984b/av-13.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a906e017b29d0eb80d9ccf7a98d19268122da792dbb68eb741cfebba156e6aed", size = 31520911 }, - { url = "https://files.pythonhosted.org/packages/e5/bb/2e03acb9b27591d97f700a3a6c27cfd1bc53fa148177747eda8a70cca1e9/av-13.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ce894d7847897da7be63277a0875bd93c51327134ac226c67978de014c7979f", size = 34048399 }, - { url = "https://files.pythonhosted.org/packages/85/44/527aa3b65947d42cfe829326026edf0cd1a8c459390076034be275616c36/av-13.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:384bcdb5fc3238a263a5a25cc9efc690859fa4148cc4b07e00fae927178db22a", size = 25779569 }, - { url = "https://files.pythonhosted.org/packages/9b/aa/4bdd8ce59173574fc6e0c282c71ee6f96fca82643d97bf172bc4cb5a5674/av-13.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:261dbc3f4b55f4f8f3375b10b2258fca7f2ab7a6365c01bc65e77a0d5327a195", size = 24268674 }, - { url = "https://files.pythonhosted.org/packages/17/b4/b267dd5bad99eed49ec6731827c6bcb5ab03864bf732a7ebb81e3df79911/av-13.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83d259ef86b9054eb914bc7c6a7f6092a6d75cb939295e70ee979cfd92a67b99", size = 19475617 }, - { url = "https://files.pythonhosted.org/packages/68/32/4209e51f54d7b54a1feb576d309c671ed1ff437b54fcc4ec68c239199e0a/av-13.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b4d3ca159eceab97e3c0fb08fe756520fb95508417f76e48198fda2a5b0806", size = 32468873 }, - { url = "https://files.pythonhosted.org/packages/b6/d8/c174da5f06b24f3c9e36f91fd02a7411c39da9ce792c17964260d4be675e/av-13.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40e8f757e373b73a2dc4640852a00cce4a4a92ef19b2e642a96d6994cd1fffbf", size = 31818484 }, - { url = "https://files.pythonhosted.org/packages/7f/22/0dd8d1d5cad415772bb707d16aea8b81cf75d340d11d3668eea43468c730/av-13.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8aaec2c0bfd024359db3821d679009d4e637e1bee0321d20f61c54ed6b20f41", size = 34398652 }, - { url = "https://files.pythonhosted.org/packages/7b/ff/48fa68888b8d5bae36d915556ff18f9e5fdc6b5ff5ae23dc4904c9713168/av-13.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ea0deab0e6a739cb742fba2a3983d8102f7516a3cdf3c46669f3cac0ed1f351", size = 25781343 }, + { url = "https://files.pythonhosted.org/packages/39/54/c4227080c9700384db90072ace70d89b6a288b3748bd2ec0e32580a49e7f/av-13.1.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:867385e6701464a5c95903e24d2e0df1c7e0dbf211ed91d0ce639cd687373e10", size = 24255112, upload_time = "2024-10-06T04:52:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/32/4a/eb9348231655ca99b200b380f4edbceff7358c927a285badcc84b18fb1c9/av-13.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb7a3f319401a46b0017771268ff4928501e77cf00b1a2aa0721e20b2fd1146e", size = 19467930, upload_time = "2024-10-06T04:52:52.118Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/48c80252bdbc3a75a54dd205a7fab8f613914009b9e5416202757208e040/av-13.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad904f860147bceaca65b0d3174a8153f35c570d465161d210f1879970b15559", size = 32207671, upload_time = "2024-10-06T04:52:55.82Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/3332c7fa8c43b65680a94f279ea3e832b5500de3a1392bac6112881e984b/av-13.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a906e017b29d0eb80d9ccf7a98d19268122da792dbb68eb741cfebba156e6aed", size = 31520911, upload_time = "2024-10-06T04:52:59.231Z" }, + { url = "https://files.pythonhosted.org/packages/e5/bb/2e03acb9b27591d97f700a3a6c27cfd1bc53fa148177747eda8a70cca1e9/av-13.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ce894d7847897da7be63277a0875bd93c51327134ac226c67978de014c7979f", size = 34048399, upload_time = "2024-10-06T04:53:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/85/44/527aa3b65947d42cfe829326026edf0cd1a8c459390076034be275616c36/av-13.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:384bcdb5fc3238a263a5a25cc9efc690859fa4148cc4b07e00fae927178db22a", size = 25779569, upload_time = "2024-10-06T04:53:07.582Z" }, + { url = "https://files.pythonhosted.org/packages/9b/aa/4bdd8ce59173574fc6e0c282c71ee6f96fca82643d97bf172bc4cb5a5674/av-13.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:261dbc3f4b55f4f8f3375b10b2258fca7f2ab7a6365c01bc65e77a0d5327a195", size = 24268674, upload_time = "2024-10-06T04:53:11.251Z" }, + { url = "https://files.pythonhosted.org/packages/17/b4/b267dd5bad99eed49ec6731827c6bcb5ab03864bf732a7ebb81e3df79911/av-13.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83d259ef86b9054eb914bc7c6a7f6092a6d75cb939295e70ee979cfd92a67b99", size = 19475617, upload_time = "2024-10-06T04:53:13.832Z" }, + { url = "https://files.pythonhosted.org/packages/68/32/4209e51f54d7b54a1feb576d309c671ed1ff437b54fcc4ec68c239199e0a/av-13.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b4d3ca159eceab97e3c0fb08fe756520fb95508417f76e48198fda2a5b0806", size = 32468873, upload_time = "2024-10-06T04:53:17.639Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d8/c174da5f06b24f3c9e36f91fd02a7411c39da9ce792c17964260d4be675e/av-13.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40e8f757e373b73a2dc4640852a00cce4a4a92ef19b2e642a96d6994cd1fffbf", size = 31818484, upload_time = "2024-10-06T04:53:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/7f/22/0dd8d1d5cad415772bb707d16aea8b81cf75d340d11d3668eea43468c730/av-13.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8aaec2c0bfd024359db3821d679009d4e637e1bee0321d20f61c54ed6b20f41", size = 34398652, upload_time = "2024-10-06T04:53:25.798Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/48fa68888b8d5bae36d915556ff18f9e5fdc6b5ff5ae23dc4904c9713168/av-13.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ea0deab0e6a739cb742fba2a3983d8102f7516a3cdf3c46669f3cac0ed1f351", size = 25781343, upload_time = "2024-10-06T04:53:29.577Z" }, ] [[package]] @@ -156,9 +156,9 @@ dependencies = [ { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/aa/7c9db8edd626f1a7d99d09ef7926f6f4fb34d5f9fa00dc394afdfe8e2a80/azure_core-1.33.0.tar.gz", hash = "sha256:f367aa07b5e3005fec2c1e184b882b0b039910733907d001c20fb08ebb8c0eb9", size = 295633 } +sdist = { url = "https://files.pythonhosted.org/packages/75/aa/7c9db8edd626f1a7d99d09ef7926f6f4fb34d5f9fa00dc394afdfe8e2a80/azure_core-1.33.0.tar.gz", hash = "sha256:f367aa07b5e3005fec2c1e184b882b0b039910733907d001c20fb08ebb8c0eb9", size = 295633, upload_time = "2025-04-03T23:51:02.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/b7/76b7e144aa53bd206bf1ce34fa75350472c3f69bf30e5c8c18bc9881035d/azure_core-1.33.0-py3-none-any.whl", hash = "sha256:9b5b6d0223a1d38c37500e6971118c1e0f13f54951e6893968b38910bc9cda8f", size = 207071 }, + { url = "https://files.pythonhosted.org/packages/07/b7/76b7e144aa53bd206bf1ce34fa75350472c3f69bf30e5c8c18bc9881035d/azure_core-1.33.0-py3-none-any.whl", hash = "sha256:9b5b6d0223a1d38c37500e6971118c1e0f13f54951e6893968b38910bc9cda8f", size = 207071, upload_time = "2025-04-03T23:51:03.806Z" }, ] [[package]] @@ -172,9 +172,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/a1/f1a683672e7a88ea0e3119f57b6c7843ed52650fdcac8bfa66ed84e86e40/azure_identity-1.21.0.tar.gz", hash = "sha256:ea22ce6e6b0f429bc1b8d9212d5b9f9877bd4c82f1724bfa910760612c07a9a6", size = 266445 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/a1/f1a683672e7a88ea0e3119f57b6c7843ed52650fdcac8bfa66ed84e86e40/azure_identity-1.21.0.tar.gz", hash = "sha256:ea22ce6e6b0f429bc1b8d9212d5b9f9877bd4c82f1724bfa910760612c07a9a6", size = 266445, upload_time = "2025-03-11T20:53:07.463Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/9f/1f9f3ef4f49729ee207a712a5971a9ca747f2ca47d9cbf13cf6953e3478a/azure_identity-1.21.0-py3-none-any.whl", hash = "sha256:258ea6325537352440f71b35c3dffe9d240eae4a5126c1b7ce5efd5766bd9fd9", size = 189190 }, + { url = "https://files.pythonhosted.org/packages/3d/9f/1f9f3ef4f49729ee207a712a5971a9ca747f2ca47d9cbf13cf6953e3478a/azure_identity-1.21.0-py3-none-any.whl", hash = "sha256:258ea6325537352440f71b35c3dffe9d240eae4a5126c1b7ce5efd5766bd9fd9", size = 189190, upload_time = "2025-03-11T20:53:09.197Z" }, ] [[package]] @@ -187,9 +187,9 @@ dependencies = [ { name = "isodate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/f764536c25cc3829d36857167f03933ce9aee2262293179075439f3cd3ad/azure_storage_blob-12.25.1.tar.gz", hash = "sha256:4f294ddc9bc47909ac66b8934bd26b50d2000278b10ad82cc109764fdc6e0e3b", size = 570541 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/f764536c25cc3829d36857167f03933ce9aee2262293179075439f3cd3ad/azure_storage_blob-12.25.1.tar.gz", hash = "sha256:4f294ddc9bc47909ac66b8934bd26b50d2000278b10ad82cc109764fdc6e0e3b", size = 570541, upload_time = "2025-03-27T17:13:05.424Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/57/33/085d9352d416e617993821b9d9488222fbb559bc15c3641d6cbd6d16d236/azure_storage_blob-12.25.1-py3-none-any.whl", hash = "sha256:1f337aab12e918ec3f1b638baada97550673911c4ceed892acc8e4e891b74167", size = 406990 }, + { url = "https://files.pythonhosted.org/packages/57/33/085d9352d416e617993821b9d9488222fbb559bc15c3641d6cbd6d16d236/azure_storage_blob-12.25.1-py3-none-any.whl", hash = "sha256:1f337aab12e918ec3f1b638baada97550673911c4ceed892acc8e4e891b74167", size = 406990, upload_time = "2025-03-27T17:13:06.879Z" }, ] [[package]] @@ -199,29 +199,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/49/f8b8eb7c8e98e28e0cca1b41988932025d59a602fb2075f737cbaecf764d/casadi-3.7.0.tar.gz", hash = "sha256:21254f17eb5551c4a938641cc1b815ff3da27271ab2c36e44a3e90ec50ba471f", size = 6027731 } +sdist = { url = "https://files.pythonhosted.org/packages/b3/49/f8b8eb7c8e98e28e0cca1b41988932025d59a602fb2075f737cbaecf764d/casadi-3.7.0.tar.gz", hash = "sha256:21254f17eb5551c4a938641cc1b815ff3da27271ab2c36e44a3e90ec50ba471f", size = 6027731, upload_time = "2025-03-29T22:31:31.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/cf/7d1dc6b16f690f1fac22a0a43684de3a03b3f631a1d8c25393f320b5f971/casadi-3.7.0-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:50bba75bb9575baa09a2e25ce3c1968803320c49b9dcc218139321dab02f0400", size = 48302536 }, - { url = "https://files.pythonhosted.org/packages/74/d2/f69156aaf4545543995c791a0f97551b66f9c9bc8d92361973ee6378ad25/casadi-3.7.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:8f138bf370603f6f820385d78882f840168e8e0866bea5de08b6b54a6e22c093", size = 44713683 }, - { url = "https://files.pythonhosted.org/packages/2e/3b/13a7d92c27543e0a2eb559f2a5acc7278f1a2ef0217772b09df50ac1c2dc/casadi-3.7.0-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:4d844a1281c7ff527115d27ee8f2c58315d0008cac835656323a6d502de27010", size = 45518255 }, - { url = "https://files.pythonhosted.org/packages/bb/88/00adcb26a0c313ebeaa2db8b2691e37418d6b7c3e06b4a90a3ac9380b91b/casadi-3.7.0-cp311-none-manylinux2014_i686.whl", hash = "sha256:b8640486db98b75a75eb05b6191d5a7f0d44774c36c07c7da327d4d740914284", size = 74329105 }, - { url = "https://files.pythonhosted.org/packages/60/64/b31fd4ce5b93f97fd16a9ba7ce8d4a8d36334a518f1ad00525340db31ff4/casadi-3.7.0-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:65d9a61660c75ff8f0c30abf52e2f9cefcdee1cbb9301e26678f64116f509ee7", size = 77330636 }, - { url = "https://files.pythonhosted.org/packages/c4/25/79bb6b866a7005186ccdfcb89e01032885a9efa90acfe2d079253685622b/casadi-3.7.0-cp311-none-win_amd64.whl", hash = "sha256:391b5ee886cde28bf813e820958bfcdef98314bd367a93c95e7bef1bf713b886", size = 50949252 }, - { url = "https://files.pythonhosted.org/packages/b8/27/6ca4c831d9131eb15ca34346398c1379a577363c264ad47983c1be65b3e1/casadi-3.7.0-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:ceb4d67c1c5cdf80f233866fcdcaf7d77523115983081fe909b70fe89fd7b708", size = 48303214 }, - { url = "https://files.pythonhosted.org/packages/b2/cb/cd8be8aeac6453e2aad96793101fa287f54f86b643d542375969738930c3/casadi-3.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:83dbc27125044e7600b04af648f24e6924d46e81afe6ff088218e4b7e9a37f08", size = 44713758 }, - { url = "https://files.pythonhosted.org/packages/a4/3d/c8a392eac772b1537f63ee16b57be87bcdef9d9bc9530b54c95b1a122960/casadi-3.7.0-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:53731171b41ca0640b76657d2320b929634a137ff30f12f985196df35411885b", size = 45517623 }, - { url = "https://files.pythonhosted.org/packages/68/d2/2acd3b8cf8fa25bca342cd539f69dc94b0fa1bf3acaa30f13848fa0f31ee/casadi-3.7.0-cp312-none-manylinux2014_i686.whl", hash = "sha256:6b30fb73d8c140fbbe51e60d00412aaefe5a7b775257a422ea244f423bd2351c", size = 74319729 }, - { url = "https://files.pythonhosted.org/packages/0f/35/ec351423c854a74884218501a431e018c6eee79461dde8e91f9ce6b4e2b5/casadi-3.7.0-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:b795371bf09ec0bfd22eaa0a1e81ce2cd3ecd811c9d370b98f6853b87e8397c9", size = 77323153 }, - { url = "https://files.pythonhosted.org/packages/d5/4b/ab605b5948795fe15b5930ecc5ac7ba72ebad116a9003c0117421cbe34a8/casadi-3.7.0-cp312-none-win_amd64.whl", hash = "sha256:e77173aad67b817ebf4320c31cef37c7d666f9895bc5970f3370b2ae6fdad587", size = 50946897 }, + { url = "https://files.pythonhosted.org/packages/83/cf/7d1dc6b16f690f1fac22a0a43684de3a03b3f631a1d8c25393f320b5f971/casadi-3.7.0-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:50bba75bb9575baa09a2e25ce3c1968803320c49b9dcc218139321dab02f0400", size = 48302536, upload_time = "2025-03-30T07:31:37.067Z" }, + { url = "https://files.pythonhosted.org/packages/74/d2/f69156aaf4545543995c791a0f97551b66f9c9bc8d92361973ee6378ad25/casadi-3.7.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:8f138bf370603f6f820385d78882f840168e8e0866bea5de08b6b54a6e22c093", size = 44713683, upload_time = "2025-03-30T07:33:37.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3b/13a7d92c27543e0a2eb559f2a5acc7278f1a2ef0217772b09df50ac1c2dc/casadi-3.7.0-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:4d844a1281c7ff527115d27ee8f2c58315d0008cac835656323a6d502de27010", size = 45518255, upload_time = "2025-03-30T07:36:03.844Z" }, + { url = "https://files.pythonhosted.org/packages/bb/88/00adcb26a0c313ebeaa2db8b2691e37418d6b7c3e06b4a90a3ac9380b91b/casadi-3.7.0-cp311-none-manylinux2014_i686.whl", hash = "sha256:b8640486db98b75a75eb05b6191d5a7f0d44774c36c07c7da327d4d740914284", size = 74329105, upload_time = "2025-03-30T07:39:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/64/b31fd4ce5b93f97fd16a9ba7ce8d4a8d36334a518f1ad00525340db31ff4/casadi-3.7.0-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:65d9a61660c75ff8f0c30abf52e2f9cefcdee1cbb9301e26678f64116f509ee7", size = 77330636, upload_time = "2025-03-30T07:46:55.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/25/79bb6b866a7005186ccdfcb89e01032885a9efa90acfe2d079253685622b/casadi-3.7.0-cp311-none-win_amd64.whl", hash = "sha256:391b5ee886cde28bf813e820958bfcdef98314bd367a93c95e7bef1bf713b886", size = 50949252, upload_time = "2025-03-30T07:49:05.348Z" }, + { url = "https://files.pythonhosted.org/packages/b8/27/6ca4c831d9131eb15ca34346398c1379a577363c264ad47983c1be65b3e1/casadi-3.7.0-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:ceb4d67c1c5cdf80f233866fcdcaf7d77523115983081fe909b70fe89fd7b708", size = 48303214, upload_time = "2025-03-30T07:51:05.246Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cb/cd8be8aeac6453e2aad96793101fa287f54f86b643d542375969738930c3/casadi-3.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:83dbc27125044e7600b04af648f24e6924d46e81afe6ff088218e4b7e9a37f08", size = 44713758, upload_time = "2025-03-30T07:52:44.766Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3d/c8a392eac772b1537f63ee16b57be87bcdef9d9bc9530b54c95b1a122960/casadi-3.7.0-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:53731171b41ca0640b76657d2320b929634a137ff30f12f985196df35411885b", size = 45517623, upload_time = "2025-03-30T07:54:25.318Z" }, + { url = "https://files.pythonhosted.org/packages/68/d2/2acd3b8cf8fa25bca342cd539f69dc94b0fa1bf3acaa30f13848fa0f31ee/casadi-3.7.0-cp312-none-manylinux2014_i686.whl", hash = "sha256:6b30fb73d8c140fbbe51e60d00412aaefe5a7b775257a422ea244f423bd2351c", size = 74319729, upload_time = "2025-03-30T07:57:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/0f/35/ec351423c854a74884218501a431e018c6eee79461dde8e91f9ce6b4e2b5/casadi-3.7.0-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:b795371bf09ec0bfd22eaa0a1e81ce2cd3ecd811c9d370b98f6853b87e8397c9", size = 77323153, upload_time = "2025-03-30T08:00:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4b/ab605b5948795fe15b5930ecc5ac7ba72ebad116a9003c0117421cbe34a8/casadi-3.7.0-cp312-none-win_amd64.whl", hash = "sha256:e77173aad67b817ebf4320c31cef37c7d666f9895bc5970f3370b2ae6fdad587", size = 50946897, upload_time = "2025-03-30T08:02:37.836Z" }, ] [[package]] name = "certifi" version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload_time = "2025-01-31T02:16:47.166Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload_time = "2025-01-31T02:16:45.015Z" }, ] [[package]] @@ -231,66 +231,66 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload_time = "2024-09-04T20:45:21.852Z" } wheels = [ - { 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 }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { 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, upload_time = "2024-09-04T20:43:51.124Z" }, + { 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, upload_time = "2024-09-04T20:43:52.872Z" }, + { 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, upload_time = "2024-09-04T20:43:56.123Z" }, + { 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, upload_time = "2024-09-04T20:43:57.891Z" }, + { 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, upload_time = "2024-09-04T20:44:00.18Z" }, + { 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, upload_time = "2024-09-04T20:44:01.585Z" }, + { 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, upload_time = "2024-09-04T20:44:03.467Z" }, + { 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, upload_time = "2024-09-04T20:44:05.023Z" }, + { 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, upload_time = "2024-09-04T20:44:06.444Z" }, + { 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, upload_time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload_time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload_time = "2024-09-04T20:44:10.873Z" }, + { 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, upload_time = "2024-09-04T20:44:12.232Z" }, + { 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, upload_time = "2024-09-04T20:44:13.739Z" }, + { 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, upload_time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload_time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload_time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload_time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload_time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload_time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload_time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload_time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload_time = "2024-09-04T20:44:27.578Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload_time = "2024-12-24T18:12:35.43Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, - { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, - { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, - { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, - { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, - { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, - { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, - { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, - { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, - { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, - { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, - { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, - { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, - { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, - { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, - { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, - { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, - { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, - { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, - { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, - { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, - { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, - { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, - { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, - { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, - { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, - { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995, upload_time = "2024-12-24T18:10:12.838Z" }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471, upload_time = "2024-12-24T18:10:14.101Z" }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831, upload_time = "2024-12-24T18:10:15.512Z" }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335, upload_time = "2024-12-24T18:10:18.369Z" }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862, upload_time = "2024-12-24T18:10:19.743Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673, upload_time = "2024-12-24T18:10:21.139Z" }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211, upload_time = "2024-12-24T18:10:22.382Z" }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039, upload_time = "2024-12-24T18:10:24.802Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939, upload_time = "2024-12-24T18:10:26.124Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075, upload_time = "2024-12-24T18:10:30.027Z" }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340, upload_time = "2024-12-24T18:10:32.679Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205, upload_time = "2024-12-24T18:10:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441, upload_time = "2024-12-24T18:10:37.574Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload_time = "2024-12-24T18:10:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload_time = "2024-12-24T18:10:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload_time = "2024-12-24T18:10:45.492Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload_time = "2024-12-24T18:10:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload_time = "2024-12-24T18:10:50.589Z" }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload_time = "2024-12-24T18:10:52.541Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload_time = "2024-12-24T18:10:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload_time = "2024-12-24T18:10:55.048Z" }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload_time = "2024-12-24T18:10:57.647Z" }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload_time = "2024-12-24T18:10:59.43Z" }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload_time = "2024-12-24T18:11:00.676Z" }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload_time = "2024-12-24T18:11:01.952Z" }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload_time = "2024-12-24T18:11:03.142Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload_time = "2024-12-24T18:12:32.852Z" }, ] [[package]] @@ -300,97 +300,100 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload_time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload_time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "cloudpickle" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113 } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload_time = "2025-01-14T17:02:05.085Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992 }, + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload_time = "2025-01-14T17:02:02.417Z" }, ] [[package]] name = "codespell" version = "2.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/e0/709453393c0ea77d007d907dd436b3ee262e28b30995ea1aa36c6ffbccaf/codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5", size = 344740 } +sdist = { url = "https://files.pythonhosted.org/packages/15/e0/709453393c0ea77d007d907dd436b3ee262e28b30995ea1aa36c6ffbccaf/codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5", size = 344740, upload_time = "2025-01-28T18:52:39.411Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/01/b394922252051e97aab231d416c86da3d8a6d781eeadcdca1082867de64e/codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425", size = 344501 }, + { url = "https://files.pythonhosted.org/packages/20/01/b394922252051e97aab231d416c86da3d8a6d781eeadcdca1082867de64e/codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425", size = 344501, upload_time = "2025-01-28T18:52:37.057Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "contourpy" -version = "1.3.1" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/c2/fc7193cc5383637ff390a712e88e4ded0452c9fbcf84abe3de5ea3df1866/contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", size = 13465753 } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload_time = "2025-04-15T17:47:53.79Z" } wheels = [ - { 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 }, - { url = "https://files.pythonhosted.org/packages/48/b2/011415f5e3f0a50b1e285a0bf78eb5d92a4df000553570f0851b6e309076/contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", size = 347271 }, - { url = "https://files.pythonhosted.org/packages/84/7d/ef19b1db0f45b151ac78c65127235239a8cf21a59d1ce8507ce03e89a30b/contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", size = 318906 }, - { url = "https://files.pythonhosted.org/packages/ba/99/6794142b90b853a9155316c8f470d2e4821fe6f086b03e372aca848227dd/contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", size = 323622 }, - { url = "https://files.pythonhosted.org/packages/3c/0f/37d2c84a900cd8eb54e105f4fa9aebd275e14e266736778bb5dccbf3bbbb/contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", size = 1266699 }, - { url = "https://files.pythonhosted.org/packages/3a/8a/deb5e11dc7d9cc8f0f9c8b29d4f062203f3af230ba83c30a6b161a6effc9/contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", size = 1326395 }, - { url = "https://files.pythonhosted.org/packages/1a/35/7e267ae7c13aaf12322ccc493531f1e7f2eb8fba2927b9d7a05ff615df7a/contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", size = 175354 }, - { url = "https://files.pythonhosted.org/packages/a1/35/c2de8823211d07e8a79ab018ef03960716c5dff6f4d5bff5af87fd682992/contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", size = 220971 }, + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload_time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload_time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload_time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload_time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload_time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload_time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload_time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload_time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload_time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload_time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload_time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload_time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload_time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload_time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload_time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload_time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload_time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload_time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload_time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload_time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload_time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload_time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload_time = "2025-04-15T17:45:24.794Z" }, ] [[package]] name = "coverage" version = "7.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872 } +sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872, upload_time = "2025-03-30T20:36:45.376Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493 }, - { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921 }, - { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556 }, - { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245 }, - { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032 }, - { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679 }, - { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852 }, - { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389 }, - { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997 }, - { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911 }, - { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684 }, - { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935 }, - { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994 }, - { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885 }, - { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142 }, - { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906 }, - { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124 }, - { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317 }, - { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170 }, - { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969 }, - { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443 }, - { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435 }, + { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493, upload_time = "2025-03-30T20:35:12.286Z" }, + { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921, upload_time = "2025-03-30T20:35:14.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556, upload_time = "2025-03-30T20:35:15.616Z" }, + { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245, upload_time = "2025-03-30T20:35:18.648Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032, upload_time = "2025-03-30T20:35:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679, upload_time = "2025-03-30T20:35:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852, upload_time = "2025-03-30T20:35:23.525Z" }, + { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389, upload_time = "2025-03-30T20:35:25.09Z" }, + { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997, upload_time = "2025-03-30T20:35:26.914Z" }, + { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911, upload_time = "2025-03-30T20:35:28.498Z" }, + { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684, upload_time = "2025-03-30T20:35:29.959Z" }, + { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935, upload_time = "2025-03-30T20:35:31.912Z" }, + { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994, upload_time = "2025-03-30T20:35:33.455Z" }, + { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885, upload_time = "2025-03-30T20:35:35.354Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142, upload_time = "2025-03-30T20:35:37.121Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906, upload_time = "2025-03-30T20:35:39.07Z" }, + { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124, upload_time = "2025-03-30T20:35:40.598Z" }, + { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317, upload_time = "2025-03-30T20:35:42.204Z" }, + { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170, upload_time = "2025-03-30T20:35:44.216Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969, upload_time = "2025-03-30T20:35:45.797Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443, upload_time = "2025-03-30T20:36:41.959Z" }, + { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435, upload_time = "2025-03-30T20:36:43.61Z" }, ] [package.optional-dependencies] @@ -402,7 +405,7 @@ toml = [ name = "crcmod" version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/b0/e595ce2a2527e169c3bcd6c33d2473c1918e0b7f6826a043ca1245dd4e5b/crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e", size = 89670 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/b0/e595ce2a2527e169c3bcd6c33d2473c1918e0b7f6826a043ca1245dd4e5b/crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e", size = 89670, upload_time = "2010-06-27T14:35:29.538Z" } [[package]] name = "cryptography" @@ -411,87 +414,87 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", size = 686989 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", size = 686989, upload_time = "2024-10-18T15:58:32.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/f3/01fdf26701a26f4b4dbc337a26883ad5bccaa6f1bbbdd29cd89e22f18a1c/cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", size = 6225303 }, - { url = "https://files.pythonhosted.org/packages/a3/01/4896f3d1b392025d4fcbecf40fdea92d3df8662123f6835d0af828d148fd/cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", size = 3760905 }, - { url = "https://files.pythonhosted.org/packages/0a/be/f9a1f673f0ed4b7f6c643164e513dbad28dd4f2dcdf5715004f172ef24b6/cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", size = 3977271 }, - { url = "https://files.pythonhosted.org/packages/4e/49/80c3a7b5514d1b416d7350830e8c422a4d667b6d9b16a9392ebfd4a5388a/cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", size = 3746606 }, - { url = "https://files.pythonhosted.org/packages/0e/16/a28ddf78ac6e7e3f25ebcef69ab15c2c6be5ff9743dd0709a69a4f968472/cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", size = 3986484 }, - { url = "https://files.pythonhosted.org/packages/01/f5/69ae8da70c19864a32b0315049866c4d411cce423ec169993d0434218762/cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", size = 3852131 }, - { url = "https://files.pythonhosted.org/packages/fd/db/e74911d95c040f9afd3612b1f732e52b3e517cb80de8bf183be0b7d413c6/cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", size = 4075647 }, - { url = "https://files.pythonhosted.org/packages/56/48/7b6b190f1462818b324e674fa20d1d5ef3e24f2328675b9b16189cbf0b3c/cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", size = 2623873 }, - { url = "https://files.pythonhosted.org/packages/eb/b1/0ebff61a004f7f89e7b65ca95f2f2375679d43d0290672f7713ee3162aff/cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", size = 3068039 }, - { url = "https://files.pythonhosted.org/packages/30/d5/c8b32c047e2e81dd172138f772e81d852c51f0f2ad2ae8a24f1122e9e9a7/cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", size = 6222984 }, - { url = "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", size = 3762968 }, - { url = "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", size = 3977754 }, - { url = "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", size = 3749458 }, - { url = "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", size = 3988220 }, - { url = "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", size = 3853898 }, - { url = "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", size = 4076592 }, - { url = "https://files.pythonhosted.org/packages/81/1e/ffcc41b3cebd64ca90b28fd58141c5f68c83d48563c88333ab660e002cd3/cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", size = 2623145 }, - { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026 }, + { url = "https://files.pythonhosted.org/packages/1f/f3/01fdf26701a26f4b4dbc337a26883ad5bccaa6f1bbbdd29cd89e22f18a1c/cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", size = 6225303, upload_time = "2024-10-18T15:57:36.753Z" }, + { url = "https://files.pythonhosted.org/packages/a3/01/4896f3d1b392025d4fcbecf40fdea92d3df8662123f6835d0af828d148fd/cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", size = 3760905, upload_time = "2024-10-18T15:57:39.166Z" }, + { url = "https://files.pythonhosted.org/packages/0a/be/f9a1f673f0ed4b7f6c643164e513dbad28dd4f2dcdf5715004f172ef24b6/cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", size = 3977271, upload_time = "2024-10-18T15:57:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/4e/49/80c3a7b5514d1b416d7350830e8c422a4d667b6d9b16a9392ebfd4a5388a/cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", size = 3746606, upload_time = "2024-10-18T15:57:42.903Z" }, + { url = "https://files.pythonhosted.org/packages/0e/16/a28ddf78ac6e7e3f25ebcef69ab15c2c6be5ff9743dd0709a69a4f968472/cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", size = 3986484, upload_time = "2024-10-18T15:57:45.434Z" }, + { url = "https://files.pythonhosted.org/packages/01/f5/69ae8da70c19864a32b0315049866c4d411cce423ec169993d0434218762/cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", size = 3852131, upload_time = "2024-10-18T15:57:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/fd/db/e74911d95c040f9afd3612b1f732e52b3e517cb80de8bf183be0b7d413c6/cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", size = 4075647, upload_time = "2024-10-18T15:57:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/56/48/7b6b190f1462818b324e674fa20d1d5ef3e24f2328675b9b16189cbf0b3c/cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", size = 2623873, upload_time = "2024-10-18T15:57:51.822Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b1/0ebff61a004f7f89e7b65ca95f2f2375679d43d0290672f7713ee3162aff/cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", size = 3068039, upload_time = "2024-10-18T15:57:54.426Z" }, + { url = "https://files.pythonhosted.org/packages/30/d5/c8b32c047e2e81dd172138f772e81d852c51f0f2ad2ae8a24f1122e9e9a7/cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", size = 6222984, upload_time = "2024-10-18T15:57:56.174Z" }, + { url = "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", size = 3762968, upload_time = "2024-10-18T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", size = 3977754, upload_time = "2024-10-18T15:58:00.683Z" }, + { url = "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", size = 3749458, upload_time = "2024-10-18T15:58:02.225Z" }, + { url = "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", size = 3988220, upload_time = "2024-10-18T15:58:04.331Z" }, + { url = "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", size = 3853898, upload_time = "2024-10-18T15:58:06.113Z" }, + { url = "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", size = 4076592, upload_time = "2024-10-18T15:58:08.673Z" }, + { url = "https://files.pythonhosted.org/packages/81/1e/ffcc41b3cebd64ca90b28fd58141c5f68c83d48563c88333ab660e002cd3/cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", size = 2623145, upload_time = "2024-10-18T15:58:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026, upload_time = "2024-10-18T15:58:11.916Z" }, ] [[package]] name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload_time = "2023-10-07T05:32:18.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload_time = "2023-10-07T05:32:16.783Z" }, ] [[package]] name = "cython" version = "3.0.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617, upload_time = "2025-02-11T09:05:50.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430 }, - { url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055 }, - { url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874 }, - { url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129 }, - { url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936 }, - { url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066 }, - { url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935 }, - { url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337 }, - { url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717 }, - { url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337 }, - { url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692 }, - { url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057 }, - { url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493 }, - { url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859 }, - { url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428 }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755 }, - { url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640 }, + { url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430, upload_time = "2025-02-11T09:06:47.253Z" }, + { url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055, upload_time = "2025-02-11T09:06:50.807Z" }, + { url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874, upload_time = "2025-02-11T09:06:54.806Z" }, + { url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129, upload_time = "2025-02-11T09:06:58.152Z" }, + { url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936, upload_time = "2025-02-11T09:07:01.592Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066, upload_time = "2025-02-11T09:07:03.961Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935, upload_time = "2025-02-11T09:07:06.947Z" }, + { url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337, upload_time = "2025-02-11T09:07:10.087Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717, upload_time = "2025-02-11T09:07:12.405Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337, upload_time = "2025-02-11T09:07:14.979Z" }, + { url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692, upload_time = "2025-02-11T09:07:17.45Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057, upload_time = "2025-02-11T09:07:22.106Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493, upload_time = "2025-02-11T09:07:25.183Z" }, + { url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859, upload_time = "2025-02-11T09:07:27.634Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428, upload_time = "2025-02-11T09:07:30.719Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755, upload_time = "2025-02-11T09:07:36.021Z" }, + { url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640, upload_time = "2025-02-11T09:05:45.648Z" }, ] [[package]] name = "dbus-next" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112 } +sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload_time = "2021-07-25T22:11:28.398Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885 }, + { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload_time = "2021-07-25T22:11:25.466Z" }, ] [[package]] name = "dictdiffer" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/7b/35cbccb7effc5d7e40f4c55e2b79399e1853041997fcda15c9ff160abba0/dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578", size = 31513 } +sdist = { url = "https://files.pythonhosted.org/packages/61/7b/35cbccb7effc5d7e40f4c55e2b79399e1853041997fcda15c9ff160abba0/dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578", size = 31513, upload_time = "2021-07-22T13:24:29.276Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/ef/4cb333825d10317a36a1154341ba37e6e9c087bac99c1990ef07ffdb376f/dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595", size = 16754 }, + { url = "https://files.pythonhosted.org/packages/47/ef/4cb333825d10317a36a1154341ba37e6e9c087bac99c1990ef07ffdb376f/dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595", size = 16754, upload_time = "2021-07-22T13:24:26.783Z" }, ] [[package]] name = "dnspython" version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload_time = "2024-10-05T20:14:59.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, + { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload_time = "2024-10-05T20:14:57.687Z" }, ] [[package]] @@ -503,107 +506,111 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657 }, + { url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657, upload_time = "2024-04-17T08:15:56.338Z" }, ] [[package]] name = "execnet" version = "2.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524 } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524, upload_time = "2024-04-08T09:04:19.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 }, + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612, upload_time = "2024-04-08T09:04:17.414Z" }, ] [[package]] name = "farama-notifications" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/2c/8384832b7a6b1fd6ba95bbdcae26e7137bb3eedc955c42fd5cdcc086cfbf/Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18", size = 2131 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/2c/8384832b7a6b1fd6ba95bbdcae26e7137bb3eedc955c42fd5cdcc086cfbf/Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18", size = 2131, upload_time = "2023-02-27T18:28:41.047Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/2c/ffc08c54c05cdce6fbed2aeebc46348dbe180c6d2c541c7af7ba0aa5f5f8/Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae", size = 2511 }, + { url = "https://files.pythonhosted.org/packages/05/2c/ffc08c54c05cdce6fbed2aeebc46348dbe180c6d2c541c7af7ba0aa5f5f8/Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae", size = 2511, upload_time = "2023-02-27T18:28:39.447Z" }, ] [[package]] name = "filelock" version = "3.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload_time = "2025-03-14T07:11:40.47Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload_time = "2025-03-14T07:11:39.145Z" }, ] [[package]] name = "fonttools" version = "4.57.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/2d/a9a0b6e3a0cf6bd502e64fc16d894269011930cabfc89aee20d1635b1441/fonttools-4.57.0.tar.gz", hash = "sha256:727ece10e065be2f9dd239d15dd5d60a66e17eac11aea47d447f9f03fdbc42de", size = 3492448 } +sdist = { url = "https://files.pythonhosted.org/packages/03/2d/a9a0b6e3a0cf6bd502e64fc16d894269011930cabfc89aee20d1635b1441/fonttools-4.57.0.tar.gz", hash = "sha256:727ece10e065be2f9dd239d15dd5d60a66e17eac11aea47d447f9f03fdbc42de", size = 3492448, upload_time = "2025-04-03T11:07:13.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/1f/e67c99aa3c6d3d2f93d956627e62a57ae0d35dc42f26611ea2a91053f6d6/fonttools-4.57.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3871349303bdec958360eedb619169a779956503ffb4543bb3e6211e09b647c4", size = 2757392 }, - { url = "https://files.pythonhosted.org/packages/aa/f1/f75770d0ddc67db504850898d96d75adde238c35313409bfcd8db4e4a5fe/fonttools-4.57.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c59375e85126b15a90fcba3443eaac58f3073ba091f02410eaa286da9ad80ed8", size = 2285609 }, - { url = "https://files.pythonhosted.org/packages/f5/d3/bc34e4953cb204bae0c50b527307dce559b810e624a733351a654cfc318e/fonttools-4.57.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967b65232e104f4b0f6370a62eb33089e00024f2ce143aecbf9755649421c683", size = 4873292 }, - { url = "https://files.pythonhosted.org/packages/41/b8/d5933559303a4ab18c799105f4c91ee0318cc95db4a2a09e300116625e7a/fonttools-4.57.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39acf68abdfc74e19de7485f8f7396fa4d2418efea239b7061d6ed6a2510c746", size = 4902503 }, - { url = "https://files.pythonhosted.org/packages/32/13/acb36bfaa316f481153ce78de1fa3926a8bad42162caa3b049e1afe2408b/fonttools-4.57.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d077f909f2343daf4495ba22bb0e23b62886e8ec7c109ee8234bdbd678cf344", size = 5077351 }, - { url = "https://files.pythonhosted.org/packages/b5/23/6d383a2ca83b7516d73975d8cca9d81a01acdcaa5e4db8579e4f3de78518/fonttools-4.57.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:46370ac47a1e91895d40e9ad48effbe8e9d9db1a4b80888095bc00e7beaa042f", size = 5275067 }, - { url = "https://files.pythonhosted.org/packages/bc/ca/31b8919c6da0198d5d522f1d26c980201378c087bdd733a359a1e7485769/fonttools-4.57.0-cp311-cp311-win32.whl", hash = "sha256:ca2aed95855506b7ae94e8f1f6217b7673c929e4f4f1217bcaa236253055cb36", size = 2158263 }, - { url = "https://files.pythonhosted.org/packages/13/4c/de2612ea2216eb45cfc8eb91a8501615dd87716feaf5f8fb65cbca576289/fonttools-4.57.0-cp311-cp311-win_amd64.whl", hash = "sha256:17168a4670bbe3775f3f3f72d23ee786bd965395381dfbb70111e25e81505b9d", size = 2204968 }, - { url = "https://files.pythonhosted.org/packages/cb/98/d4bc42d43392982eecaaca117d79845734d675219680cd43070bb001bc1f/fonttools-4.57.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:889e45e976c74abc7256d3064aa7c1295aa283c6bb19810b9f8b604dfe5c7f31", size = 2751824 }, - { url = "https://files.pythonhosted.org/packages/1a/62/7168030eeca3742fecf45f31e63b5ef48969fa230a672216b805f1d61548/fonttools-4.57.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0425c2e052a5f1516c94e5855dbda706ae5a768631e9fcc34e57d074d1b65b92", size = 2283072 }, - { url = "https://files.pythonhosted.org/packages/5d/82/121a26d9646f0986ddb35fbbaf58ef791c25b59ecb63ffea2aab0099044f/fonttools-4.57.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44c26a311be2ac130f40a96769264809d3b0cb297518669db437d1cc82974888", size = 4788020 }, - { url = "https://files.pythonhosted.org/packages/5b/26/e0f2fb662e022d565bbe280a3cfe6dafdaabf58889ff86fdef2d31ff1dde/fonttools-4.57.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c41ba992df5b8d680b89fd84c6a1f2aca2b9f1ae8a67400c8930cd4ea115f6", size = 4859096 }, - { url = "https://files.pythonhosted.org/packages/9e/44/9075e323347b1891cdece4b3f10a3b84a8f4c42a7684077429d9ce842056/fonttools-4.57.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea1e9e43ca56b0c12440a7c689b1350066595bebcaa83baad05b8b2675129d98", size = 4964356 }, - { url = "https://files.pythonhosted.org/packages/48/28/caa8df32743462fb966be6de6a79d7f30393859636d7732e82efa09fbbb4/fonttools-4.57.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84fd56c78d431606332a0627c16e2a63d243d0d8b05521257d77c6529abe14d8", size = 5226546 }, - { url = "https://files.pythonhosted.org/packages/f6/46/95ab0f0d2e33c5b1a4fc1c0efe5e286ba9359602c0a9907adb1faca44175/fonttools-4.57.0-cp312-cp312-win32.whl", hash = "sha256:f4376819c1c778d59e0a31db5dc6ede854e9edf28bbfa5b756604727f7f800ac", size = 2146776 }, - { url = "https://files.pythonhosted.org/packages/06/5d/1be5424bb305880e1113631f49a55ea7c7da3a5fe02608ca7c16a03a21da/fonttools-4.57.0-cp312-cp312-win_amd64.whl", hash = "sha256:57e30241524879ea10cdf79c737037221f77cc126a8cdc8ff2c94d4a522504b9", size = 2193956 }, - { url = "https://files.pythonhosted.org/packages/90/27/45f8957c3132917f91aaa56b700bcfc2396be1253f685bd5c68529b6f610/fonttools-4.57.0-py3-none-any.whl", hash = "sha256:3122c604a675513c68bd24c6a8f9091f1c2376d18e8f5fe5a101746c81b3e98f", size = 1093605 }, + { url = "https://files.pythonhosted.org/packages/81/1f/e67c99aa3c6d3d2f93d956627e62a57ae0d35dc42f26611ea2a91053f6d6/fonttools-4.57.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3871349303bdec958360eedb619169a779956503ffb4543bb3e6211e09b647c4", size = 2757392, upload_time = "2025-04-03T11:05:45.715Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f1/f75770d0ddc67db504850898d96d75adde238c35313409bfcd8db4e4a5fe/fonttools-4.57.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c59375e85126b15a90fcba3443eaac58f3073ba091f02410eaa286da9ad80ed8", size = 2285609, upload_time = "2025-04-03T11:05:47.977Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d3/bc34e4953cb204bae0c50b527307dce559b810e624a733351a654cfc318e/fonttools-4.57.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967b65232e104f4b0f6370a62eb33089e00024f2ce143aecbf9755649421c683", size = 4873292, upload_time = "2025-04-03T11:05:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/b8/d5933559303a4ab18c799105f4c91ee0318cc95db4a2a09e300116625e7a/fonttools-4.57.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39acf68abdfc74e19de7485f8f7396fa4d2418efea239b7061d6ed6a2510c746", size = 4902503, upload_time = "2025-04-03T11:05:52.17Z" }, + { url = "https://files.pythonhosted.org/packages/32/13/acb36bfaa316f481153ce78de1fa3926a8bad42162caa3b049e1afe2408b/fonttools-4.57.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d077f909f2343daf4495ba22bb0e23b62886e8ec7c109ee8234bdbd678cf344", size = 5077351, upload_time = "2025-04-03T11:05:54.162Z" }, + { url = "https://files.pythonhosted.org/packages/b5/23/6d383a2ca83b7516d73975d8cca9d81a01acdcaa5e4db8579e4f3de78518/fonttools-4.57.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:46370ac47a1e91895d40e9ad48effbe8e9d9db1a4b80888095bc00e7beaa042f", size = 5275067, upload_time = "2025-04-03T11:05:57.375Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ca/31b8919c6da0198d5d522f1d26c980201378c087bdd733a359a1e7485769/fonttools-4.57.0-cp311-cp311-win32.whl", hash = "sha256:ca2aed95855506b7ae94e8f1f6217b7673c929e4f4f1217bcaa236253055cb36", size = 2158263, upload_time = "2025-04-03T11:05:59.567Z" }, + { url = "https://files.pythonhosted.org/packages/13/4c/de2612ea2216eb45cfc8eb91a8501615dd87716feaf5f8fb65cbca576289/fonttools-4.57.0-cp311-cp311-win_amd64.whl", hash = "sha256:17168a4670bbe3775f3f3f72d23ee786bd965395381dfbb70111e25e81505b9d", size = 2204968, upload_time = "2025-04-03T11:06:02.16Z" }, + { url = "https://files.pythonhosted.org/packages/cb/98/d4bc42d43392982eecaaca117d79845734d675219680cd43070bb001bc1f/fonttools-4.57.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:889e45e976c74abc7256d3064aa7c1295aa283c6bb19810b9f8b604dfe5c7f31", size = 2751824, upload_time = "2025-04-03T11:06:03.782Z" }, + { url = "https://files.pythonhosted.org/packages/1a/62/7168030eeca3742fecf45f31e63b5ef48969fa230a672216b805f1d61548/fonttools-4.57.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0425c2e052a5f1516c94e5855dbda706ae5a768631e9fcc34e57d074d1b65b92", size = 2283072, upload_time = "2025-04-03T11:06:05.533Z" }, + { url = "https://files.pythonhosted.org/packages/5d/82/121a26d9646f0986ddb35fbbaf58ef791c25b59ecb63ffea2aab0099044f/fonttools-4.57.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44c26a311be2ac130f40a96769264809d3b0cb297518669db437d1cc82974888", size = 4788020, upload_time = "2025-04-03T11:06:07.249Z" }, + { url = "https://files.pythonhosted.org/packages/5b/26/e0f2fb662e022d565bbe280a3cfe6dafdaabf58889ff86fdef2d31ff1dde/fonttools-4.57.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c41ba992df5b8d680b89fd84c6a1f2aca2b9f1ae8a67400c8930cd4ea115f6", size = 4859096, upload_time = "2025-04-03T11:06:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/9e/44/9075e323347b1891cdece4b3f10a3b84a8f4c42a7684077429d9ce842056/fonttools-4.57.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea1e9e43ca56b0c12440a7c689b1350066595bebcaa83baad05b8b2675129d98", size = 4964356, upload_time = "2025-04-03T11:06:11.294Z" }, + { url = "https://files.pythonhosted.org/packages/48/28/caa8df32743462fb966be6de6a79d7f30393859636d7732e82efa09fbbb4/fonttools-4.57.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84fd56c78d431606332a0627c16e2a63d243d0d8b05521257d77c6529abe14d8", size = 5226546, upload_time = "2025-04-03T11:06:13.6Z" }, + { url = "https://files.pythonhosted.org/packages/f6/46/95ab0f0d2e33c5b1a4fc1c0efe5e286ba9359602c0a9907adb1faca44175/fonttools-4.57.0-cp312-cp312-win32.whl", hash = "sha256:f4376819c1c778d59e0a31db5dc6ede854e9edf28bbfa5b756604727f7f800ac", size = 2146776, upload_time = "2025-04-03T11:06:15.643Z" }, + { url = "https://files.pythonhosted.org/packages/06/5d/1be5424bb305880e1113631f49a55ea7c7da3a5fe02608ca7c16a03a21da/fonttools-4.57.0-cp312-cp312-win_amd64.whl", hash = "sha256:57e30241524879ea10cdf79c737037221f77cc126a8cdc8ff2c94d4a522504b9", size = 2193956, upload_time = "2025-04-03T11:06:17.534Z" }, + { url = "https://files.pythonhosted.org/packages/90/27/45f8957c3132917f91aaa56b700bcfc2396be1253f685bd5c68529b6f610/fonttools-4.57.0-py3-none-any.whl", hash = "sha256:3122c604a675513c68bd24c6a8f9091f1c2376d18e8f5fe5a101746c81b3e98f", size = 1093605, upload_time = "2025-04-03T11:07:11.341Z" }, ] [[package]] name = "frozenlist" -version = "1.5.0" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831, upload_time = "2025-04-17T22:38:53.099Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/43/0bed28bf5eb1c9e4301003b74453b8e7aa85fb293b31dde352aac528dafc/frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30", size = 94987 }, - { url = "https://files.pythonhosted.org/packages/bb/bf/b74e38f09a246e8abbe1e90eb65787ed745ccab6eaa58b9c9308e052323d/frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5", size = 54584 }, - { url = "https://files.pythonhosted.org/packages/2c/31/ab01375682f14f7613a1ade30149f684c84f9b8823a4391ed950c8285656/frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778", size = 52499 }, - { url = "https://files.pythonhosted.org/packages/98/a8/d0ac0b9276e1404f58fec3ab6e90a4f76b778a49373ccaf6a563f100dfbc/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a", size = 276357 }, - { url = "https://files.pythonhosted.org/packages/ad/c9/c7761084fa822f07dac38ac29f841d4587570dd211e2262544aa0b791d21/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869", size = 287516 }, - { url = "https://files.pythonhosted.org/packages/a1/ff/cd7479e703c39df7bdab431798cef89dc75010d8aa0ca2514c5b9321db27/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d", size = 283131 }, - { url = "https://files.pythonhosted.org/packages/59/a0/370941beb47d237eca4fbf27e4e91389fd68699e6f4b0ebcc95da463835b/frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45", size = 261320 }, - { url = "https://files.pythonhosted.org/packages/b8/5f/c10123e8d64867bc9b4f2f510a32042a306ff5fcd7e2e09e5ae5100ee333/frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d", size = 274877 }, - { url = "https://files.pythonhosted.org/packages/fa/79/38c505601ae29d4348f21706c5d89755ceded02a745016ba2f58bd5f1ea6/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3", size = 269592 }, - { url = "https://files.pythonhosted.org/packages/19/e2/39f3a53191b8204ba9f0bb574b926b73dd2efba2a2b9d2d730517e8f7622/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a", size = 265934 }, - { url = "https://files.pythonhosted.org/packages/d5/c9/3075eb7f7f3a91f1a6b00284af4de0a65a9ae47084930916f5528144c9dd/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9", size = 283859 }, - { url = "https://files.pythonhosted.org/packages/05/f5/549f44d314c29408b962fa2b0e69a1a67c59379fb143b92a0a065ffd1f0f/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2", size = 287560 }, - { url = "https://files.pythonhosted.org/packages/9d/f8/cb09b3c24a3eac02c4c07a9558e11e9e244fb02bf62c85ac2106d1eb0c0b/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf", size = 277150 }, - { url = "https://files.pythonhosted.org/packages/37/48/38c2db3f54d1501e692d6fe058f45b6ad1b358d82cd19436efab80cfc965/frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942", size = 45244 }, - { url = "https://files.pythonhosted.org/packages/ca/8c/2ddffeb8b60a4bce3b196c32fcc30d8830d4615e7b492ec2071da801b8ad/frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d", size = 51634 }, - { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 }, - { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 }, - { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 }, - { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 }, - { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 }, - { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 }, - { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 }, - { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 }, - { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 }, - { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 }, - { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 }, - { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 }, - { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 }, - { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 }, - { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 }, - { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, + { url = "https://files.pythonhosted.org/packages/53/b5/bc883b5296ec902115c00be161da93bf661199c465ec4c483feec6ea4c32/frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d", size = 160912, upload_time = "2025-04-17T22:36:17.235Z" }, + { url = "https://files.pythonhosted.org/packages/6f/93/51b058b563d0704b39c56baa222828043aafcac17fd3734bec5dbeb619b1/frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0", size = 124315, upload_time = "2025-04-17T22:36:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e0/46cd35219428d350558b874d595e132d1c17a9471a1bd0d01d518a261e7c/frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe", size = 122230, upload_time = "2025-04-17T22:36:20.6Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0f/7ad2ce928ad06d6dd26a61812b959ded573d3e9d0ee6109d96c2be7172e9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba", size = 314842, upload_time = "2025-04-17T22:36:22.088Z" }, + { url = "https://files.pythonhosted.org/packages/34/76/98cbbd8a20a5c3359a2004ae5e5b216af84a150ccbad67c8f8f30fb2ea91/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595", size = 304919, upload_time = "2025-04-17T22:36:24.247Z" }, + { url = "https://files.pythonhosted.org/packages/9a/fa/258e771ce3a44348c05e6b01dffc2bc67603fba95761458c238cd09a2c77/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a", size = 324074, upload_time = "2025-04-17T22:36:26.291Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a4/047d861fd8c538210e12b208c0479912273f991356b6bdee7ea8356b07c9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626", size = 321292, upload_time = "2025-04-17T22:36:27.909Z" }, + { url = "https://files.pythonhosted.org/packages/c0/25/cfec8af758b4525676cabd36efcaf7102c1348a776c0d1ad046b8a7cdc65/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff", size = 301569, upload_time = "2025-04-17T22:36:29.448Z" }, + { url = "https://files.pythonhosted.org/packages/87/2f/0c819372fa9f0c07b153124bf58683b8d0ca7bb73ea5ccde9b9ef1745beb/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a", size = 313625, upload_time = "2025-04-17T22:36:31.55Z" }, + { url = "https://files.pythonhosted.org/packages/50/5f/f0cf8b0fdedffdb76b3745aa13d5dbe404d63493cc211ce8250f2025307f/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0", size = 312523, upload_time = "2025-04-17T22:36:33.078Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6c/38c49108491272d3e84125bbabf2c2d0b304899b52f49f0539deb26ad18d/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606", size = 322657, upload_time = "2025-04-17T22:36:34.688Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4b/3bd3bad5be06a9d1b04b1c22be80b5fe65b502992d62fab4bdb25d9366ee/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584", size = 303414, upload_time = "2025-04-17T22:36:36.363Z" }, + { url = "https://files.pythonhosted.org/packages/5b/89/7e225a30bef6e85dbfe22622c24afe932e9444de3b40d58b1ea589a14ef8/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a", size = 320321, upload_time = "2025-04-17T22:36:38.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/72/7e3acef4dd9e86366cb8f4d8f28e852c2b7e116927e9722b31a6f71ea4b0/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1", size = 323975, upload_time = "2025-04-17T22:36:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/d8/85/e5da03d20507e13c66ce612c9792b76811b7a43e3320cce42d95b85ac755/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e", size = 316553, upload_time = "2025-04-17T22:36:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/ac/8e/6c609cbd0580ae8a0661c408149f196aade7d325b1ae7adc930501b81acb/frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860", size = 115511, upload_time = "2025-04-17T22:36:44.067Z" }, + { url = "https://files.pythonhosted.org/packages/f2/13/a84804cfde6de12d44ed48ecbf777ba62b12ff09e761f76cdd1ff9e14bb1/frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603", size = 120863, upload_time = "2025-04-17T22:36:45.465Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8a/289b7d0de2fbac832ea80944d809759976f661557a38bb8e77db5d9f79b7/frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1", size = 160193, upload_time = "2025-04-17T22:36:47.382Z" }, + { url = "https://files.pythonhosted.org/packages/19/80/2fd17d322aec7f430549f0669f599997174f93ee17929ea5b92781ec902c/frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29", size = 123831, upload_time = "2025-04-17T22:36:49.401Z" }, + { url = "https://files.pythonhosted.org/packages/99/06/f5812da431273f78c6543e0b2f7de67dfd65eb0a433978b2c9c63d2205e4/frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25", size = 121862, upload_time = "2025-04-17T22:36:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/d0/31/9e61c6b5fc493cf24d54881731204d27105234d09878be1a5983182cc4a5/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576", size = 316361, upload_time = "2025-04-17T22:36:53.402Z" }, + { url = "https://files.pythonhosted.org/packages/9d/55/22ca9362d4f0222324981470fd50192be200154d51509ee6eb9baa148e96/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8", size = 307115, upload_time = "2025-04-17T22:36:55.016Z" }, + { url = "https://files.pythonhosted.org/packages/ae/39/4fff42920a57794881e7bb3898dc7f5f539261711ea411b43bba3cde8b79/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9", size = 322505, upload_time = "2025-04-17T22:36:57.12Z" }, + { url = "https://files.pythonhosted.org/packages/55/f2/88c41f374c1e4cf0092a5459e5f3d6a1e17ed274c98087a76487783df90c/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e", size = 322666, upload_time = "2025-04-17T22:36:58.735Z" }, + { url = "https://files.pythonhosted.org/packages/75/51/034eeb75afdf3fd03997856195b500722c0b1a50716664cde64e28299c4b/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590", size = 302119, upload_time = "2025-04-17T22:37:00.512Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a6/564ecde55ee633270a793999ef4fd1d2c2b32b5a7eec903b1012cb7c5143/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103", size = 316226, upload_time = "2025-04-17T22:37:02.102Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/6c0682c32377f402b8a6174fb16378b683cf6379ab4d2827c580892ab3c7/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c", size = 312788, upload_time = "2025-04-17T22:37:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b8/10fbec38f82c5d163ca1750bfff4ede69713badf236a016781cf1f10a0f0/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821", size = 325914, upload_time = "2025-04-17T22:37:05.213Z" }, + { url = "https://files.pythonhosted.org/packages/62/ca/2bf4f3a1bd40cdedd301e6ecfdbb291080d5afc5f9ce350c0739f773d6b9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70", size = 305283, upload_time = "2025-04-17T22:37:06.985Z" }, + { url = "https://files.pythonhosted.org/packages/09/64/20cc13ccf94abc2a1f482f74ad210703dc78a590d0b805af1c9aa67f76f9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f", size = 319264, upload_time = "2025-04-17T22:37:08.618Z" }, + { url = "https://files.pythonhosted.org/packages/20/ff/86c6a2bbe98cfc231519f5e6d712a0898488ceac804a917ce014f32e68f6/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046", size = 326482, upload_time = "2025-04-17T22:37:10.196Z" }, + { url = "https://files.pythonhosted.org/packages/2f/da/8e381f66367d79adca245d1d71527aac774e30e291d41ef161ce2d80c38e/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770", size = 318248, upload_time = "2025-04-17T22:37:12.284Z" }, + { url = "https://files.pythonhosted.org/packages/39/24/1a1976563fb476ab6f0fa9fefaac7616a4361dbe0461324f9fd7bf425dbe/frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc", size = 115161, upload_time = "2025-04-17T22:37:13.902Z" }, + { url = "https://files.pythonhosted.org/packages/80/2e/fb4ed62a65f8cd66044706b1013f0010930d8cbb0729a2219561ea075434/frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878", size = 120548, upload_time = "2025-04-17T22:37:15.326Z" }, + { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404, upload_time = "2025-04-17T22:38:51.668Z" }, ] [[package]] name = "future-fstrings" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5d/e2/3874574cce18a2e3608abfe5b4b5b3c9765653c464f5da18df8971cf501d/future_fstrings-1.2.0.tar.gz", hash = "sha256:6cf41cbe97c398ab5a81168ce0dbb8ad95862d3caf23c21e4430627b90844089", size = 5786 } +sdist = { url = "https://files.pythonhosted.org/packages/5d/e2/3874574cce18a2e3608abfe5b4b5b3c9765653c464f5da18df8971cf501d/future_fstrings-1.2.0.tar.gz", hash = "sha256:6cf41cbe97c398ab5a81168ce0dbb8ad95862d3caf23c21e4430627b90844089", size = 5786, upload_time = "2019-06-16T03:04:42.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6d/ea1d52e9038558dd37f5d30647eb9f07888c164960a5d4daa5f970c6da25/future_fstrings-1.2.0-py2.py3-none-any.whl", hash = "sha256:90e49598b553d8746c4dc7d9442e0359d038c3039d802c91c0a55505da318c63", size = 6138 }, + { url = "https://files.pythonhosted.org/packages/ab/6d/ea1d52e9038558dd37f5d30647eb9f07888c164960a5d4daa5f970c6da25/future_fstrings-1.2.0-py2.py3-none-any.whl", hash = "sha256:90e49598b553d8746c4dc7d9442e0359d038c3039d802c91c0a55505da318c63", size = 6138, upload_time = "2019-06-16T03:04:40.395Z" }, ] [[package]] @@ -613,29 +620,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload_time = "2022-05-02T15:47:16.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload_time = "2022-05-02T15:47:14.552Z" }, ] [[package]] name = "google-crc32c" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495 } +sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload_time = "2025-03-26T14:29:13.32Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468 }, - { url = "https://files.pythonhosted.org/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313 }, - { url = "https://files.pythonhosted.org/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048 }, - { url = "https://files.pythonhosted.org/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669 }, - { url = "https://files.pythonhosted.org/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476 }, - { url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470 }, - { url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315 }, - { url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180 }, - { url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794 }, - { url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477 }, - { url = "https://files.pythonhosted.org/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241 }, - { url = "https://files.pythonhosted.org/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048 }, + { url = "https://files.pythonhosted.org/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468, upload_time = "2025-03-26T14:32:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313, upload_time = "2025-03-26T14:57:38.758Z" }, + { url = "https://files.pythonhosted.org/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048, upload_time = "2025-03-26T14:41:30.679Z" }, + { url = "https://files.pythonhosted.org/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669, upload_time = "2025-03-26T14:41:31.432Z" }, + { url = "https://files.pythonhosted.org/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476, upload_time = "2025-03-26T14:29:10.211Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470, upload_time = "2025-03-26T14:34:31.655Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315, upload_time = "2025-03-26T15:01:54.634Z" }, + { url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180, upload_time = "2025-03-26T14:41:32.168Z" }, + { url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794, upload_time = "2025-03-26T14:41:33.264Z" }, + { url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477, upload_time = "2025-03-26T14:29:10.94Z" }, + { url = "https://files.pythonhosted.org/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241, upload_time = "2025-03-26T14:41:45.898Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048, upload_time = "2025-03-26T14:41:46.696Z" }, ] [[package]] @@ -648,9 +655,9 @@ dependencies = [ { name = "numpy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326 } +sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326, upload_time = "2025-03-06T16:30:36.428Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/68/2bdc7b46b5f543dd865575f9d19716866bdb76e50dd33b71ed1a3dd8bb42/gymnasium-1.1.1-py3-none-any.whl", hash = "sha256:9c167ec0a2b388666e37f63b2849cd2552f7f5b71938574c637bb36487eb928a", size = 965410 }, + { url = "https://files.pythonhosted.org/packages/f9/68/2bdc7b46b5f543dd865575f9d19716866bdb76e50dd33b71ed1a3dd8bb42/gymnasium-1.1.1-py3-none-any.whl", hash = "sha256:9c167ec0a2b388666e37f63b2849cd2552f7f5b71938574c637bb36487eb928a", size = 965410, upload_time = "2025-03-06T16:30:34.443Z" }, ] [[package]] @@ -661,54 +668,54 @@ dependencies = [ { name = "attrs" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/f2/f77da8271b1abb630cb2090ead2f5aa4acc9639d632e8e68187f52527e4b/hypothesis-6.47.5.tar.gz", hash = "sha256:e0c1e253fc97e7ecdb9e2bbff2cf815d8739e0d1d3d093d67c3af5bb6a7211b0", size = 326641 } +sdist = { url = "https://files.pythonhosted.org/packages/45/f2/f77da8271b1abb630cb2090ead2f5aa4acc9639d632e8e68187f52527e4b/hypothesis-6.47.5.tar.gz", hash = "sha256:e0c1e253fc97e7ecdb9e2bbff2cf815d8739e0d1d3d093d67c3af5bb6a7211b0", size = 326641, upload_time = "2022-06-25T20:58:48.926Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/a7/389bbaade2cbbb2534cb2715986041ed01c6d792152c527e71f7f68e93b5/hypothesis-6.47.5-py3-none-any.whl", hash = "sha256:87049b781ee11ec1c7948565b889ab02e428a1e32d427ab4de8fdb3649242d06", size = 387311 }, + { url = "https://files.pythonhosted.org/packages/d3/a7/389bbaade2cbbb2534cb2715986041ed01c6d792152c527e71f7f68e93b5/hypothesis-6.47.5-py3-none-any.whl", hash = "sha256:87049b781ee11ec1c7948565b889ab02e428a1e32d427ab4de8fdb3649242d06", size = 387311, upload_time = "2022-06-25T20:58:45.281Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload_time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" }, ] [[package]] name = "ifaddr" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/fb4c578f4a3256561548cd825646680edcadb9440f3f68add95ade1eb791/ifaddr-0.2.0.tar.gz", hash = "sha256:cc0cbfcaabf765d44595825fb96a99bb12c79716b73b44330ea38ee2b0c4aed4", size = 10485 } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/fb4c578f4a3256561548cd825646680edcadb9440f3f68add95ade1eb791/ifaddr-0.2.0.tar.gz", hash = "sha256:cc0cbfcaabf765d44595825fb96a99bb12c79716b73b44330ea38ee2b0c4aed4", size = 10485, upload_time = "2022-06-15T21:40:27.561Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/1f/19ebc343cc71a7ffa78f17018535adc5cbdd87afb31d7c34874680148b32/ifaddr-0.2.0-py3-none-any.whl", hash = "sha256:085e0305cfe6f16ab12d72e2024030f5d52674afad6911bb1eee207177b8a748", size = 12314 }, + { url = "https://files.pythonhosted.org/packages/9c/1f/19ebc343cc71a7ffa78f17018535adc5cbdd87afb31d7c34874680148b32/ifaddr-0.2.0-py3-none-any.whl", hash = "sha256:085e0305cfe6f16ab12d72e2024030f5d52674afad6911bb1eee207177b8a748", size = 12314, upload_time = "2022-06-15T21:40:25.756Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload_time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload_time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "inputs" version = "0.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/cd/5f434220920f76eb73d19bb7aab8d857445f40aa642718e6e51e850cd663/inputs-0.5.tar.gz", hash = "sha256:a31d5b96a3525f1232f326be9e7ce8ccaf873c6b1fb84d9f3c9bc3d79b23eae4", size = 33393 } +sdist = { url = "https://files.pythonhosted.org/packages/d1/cd/5f434220920f76eb73d19bb7aab8d857445f40aa642718e6e51e850cd663/inputs-0.5.tar.gz", hash = "sha256:a31d5b96a3525f1232f326be9e7ce8ccaf873c6b1fb84d9f3c9bc3d79b23eae4", size = 33393, upload_time = "2018-10-05T22:38:14.206Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/94/040a0d9c81f018c39bd887b7b825013b024deb0a6c795f9524797e2cd41b/inputs-0.5-py2.py3-none-any.whl", hash = "sha256:13f894564e52134cf1e3862b1811da034875eb1f2b62e6021e3776e9669a96ec", size = 33630 }, + { url = "https://files.pythonhosted.org/packages/d0/94/040a0d9c81f018c39bd887b7b825013b024deb0a6c795f9524797e2cd41b/inputs-0.5-py2.py3-none-any.whl", hash = "sha256:13f894564e52134cf1e3862b1811da034875eb1f2b62e6021e3776e9669a96ec", size = 33630, upload_time = "2018-10-05T22:38:28.28Z" }, ] [[package]] name = "isodate" version = "0.7.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705 } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload_time = "2024-10-08T23:04:11.5Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320 }, + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload_time = "2024-10-08T23:04:09.501Z" }, ] [[package]] @@ -718,198 +725,198 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload_time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload_time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "json-rpc" version = "1.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/9e/59f4a5b7855ced7346ebf40a2e9a8942863f644378d956f68bcef2c88b90/json-rpc-1.15.0.tar.gz", hash = "sha256:e6441d56c1dcd54241c937d0a2dcd193bdf0bdc539b5316524713f554b7f85b9", size = 28854 } +sdist = { url = "https://files.pythonhosted.org/packages/6d/9e/59f4a5b7855ced7346ebf40a2e9a8942863f644378d956f68bcef2c88b90/json-rpc-1.15.0.tar.gz", hash = "sha256:e6441d56c1dcd54241c937d0a2dcd193bdf0bdc539b5316524713f554b7f85b9", size = 28854, upload_time = "2023-06-11T09:45:49.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/9e/820c4b086ad01ba7d77369fb8b11470a01fac9b4977f02e18659cf378b6b/json_rpc-1.15.0-py2.py3-none-any.whl", hash = "sha256:4a4668bbbe7116feb4abbd0f54e64a4adcf4b8f648f19ffa0848ad0f6606a9bf", size = 39450 }, + { url = "https://files.pythonhosted.org/packages/94/9e/820c4b086ad01ba7d77369fb8b11470a01fac9b4977f02e18659cf378b6b/json_rpc-1.15.0-py2.py3-none-any.whl", hash = "sha256:4a4668bbbe7116feb4abbd0f54e64a4adcf4b8f648f19ffa0848ad0f6606a9bf", size = 39450, upload_time = "2023-06-11T09:45:47.136Z" }, ] [[package]] name = "kiwisolver" version = "1.4.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538 } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538, upload_time = "2024-12-24T18:30:51.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635 }, - { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717 }, - { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413 }, - { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994 }, - { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804 }, - { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690 }, - { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839 }, - { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109 }, - { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269 }, - { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468 }, - { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394 }, - { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901 }, - { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306 }, - { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966 }, - { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311 }, - { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152 }, - { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555 }, - { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067 }, - { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443 }, - { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728 }, - { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388 }, - { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849 }, - { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533 }, - { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898 }, - { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605 }, - { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801 }, - { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077 }, - { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410 }, - { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853 }, - { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424 }, + { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635, upload_time = "2024-12-24T18:28:51.826Z" }, + { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717, upload_time = "2024-12-24T18:28:54.256Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413, upload_time = "2024-12-24T18:28:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994, upload_time = "2024-12-24T18:28:57.493Z" }, + { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804, upload_time = "2024-12-24T18:29:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690, upload_time = "2024-12-24T18:29:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839, upload_time = "2024-12-24T18:29:02.685Z" }, + { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109, upload_time = "2024-12-24T18:29:04.113Z" }, + { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269, upload_time = "2024-12-24T18:29:05.488Z" }, + { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468, upload_time = "2024-12-24T18:29:06.79Z" }, + { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394, upload_time = "2024-12-24T18:29:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901, upload_time = "2024-12-24T18:29:09.653Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306, upload_time = "2024-12-24T18:29:12.644Z" }, + { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966, upload_time = "2024-12-24T18:29:14.089Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311, upload_time = "2024-12-24T18:29:15.892Z" }, + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152, upload_time = "2024-12-24T18:29:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555, upload_time = "2024-12-24T18:29:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067, upload_time = "2024-12-24T18:29:20.096Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443, upload_time = "2024-12-24T18:29:22.843Z" }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728, upload_time = "2024-12-24T18:29:24.463Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388, upload_time = "2024-12-24T18:29:25.776Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849, upload_time = "2024-12-24T18:29:27.202Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533, upload_time = "2024-12-24T18:29:28.638Z" }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898, upload_time = "2024-12-24T18:29:30.368Z" }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605, upload_time = "2024-12-24T18:29:33.151Z" }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801, upload_time = "2024-12-24T18:29:34.584Z" }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077, upload_time = "2024-12-24T18:29:36.138Z" }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload_time = "2024-12-24T18:29:39.991Z" }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload_time = "2024-12-24T18:29:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload_time = "2024-12-24T18:29:44.38Z" }, ] [[package]] name = "libusb1" version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/7f/c59ad56d1bca8fa4321d1bb77ba4687775751a4deceec14943a44da18ca0/libusb1-3.3.1.tar.gz", hash = "sha256:3951d360f2daf0e0eacf839e15d2d1d2f4f5e7830231eb3188eeffef2dd17bad", size = 107600 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/7f/c59ad56d1bca8fa4321d1bb77ba4687775751a4deceec14943a44da18ca0/libusb1-3.3.1.tar.gz", hash = "sha256:3951d360f2daf0e0eacf839e15d2d1d2f4f5e7830231eb3188eeffef2dd17bad", size = 107600, upload_time = "2025-03-24T05:36:47.834Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/f7/4577cfc55c9520ecab5563173e83235382ac7980c8c2c08d6c9f7ef9e615/libusb1-3.3.1-py3-none-any.whl", hash = "sha256:808c9362299dcee01651aa87e71e9d681ccedb27fc4dbd70aaf14e245fb855f1", size = 67243 }, - { url = "https://files.pythonhosted.org/packages/6a/60/d3fd4831c601f063a16fc59f465ef4c1108247b07fbff371a982bd1bac45/libusb1-3.3.1-py3-none-win32.whl", hash = "sha256:0ef69825173ce74af34444754c081cc324233edc6acc405658b3ad784833e076", size = 129576 }, - { url = "https://files.pythonhosted.org/packages/94/6d/344a164d32d65d503ffe9201cd74cf13a020099dc446554d1e50b07f167b/libusb1-3.3.1-py3-none-win_amd64.whl", hash = "sha256:6e21b772d80d6487fbb55d3d2141218536db302da82f1983754e96c72781c102", size = 141080 }, + { url = "https://files.pythonhosted.org/packages/10/f7/4577cfc55c9520ecab5563173e83235382ac7980c8c2c08d6c9f7ef9e615/libusb1-3.3.1-py3-none-any.whl", hash = "sha256:808c9362299dcee01651aa87e71e9d681ccedb27fc4dbd70aaf14e245fb855f1", size = 67243, upload_time = "2025-03-24T05:36:42.312Z" }, + { url = "https://files.pythonhosted.org/packages/6a/60/d3fd4831c601f063a16fc59f465ef4c1108247b07fbff371a982bd1bac45/libusb1-3.3.1-py3-none-win32.whl", hash = "sha256:0ef69825173ce74af34444754c081cc324233edc6acc405658b3ad784833e076", size = 129576, upload_time = "2025-03-24T05:36:45.202Z" }, + { url = "https://files.pythonhosted.org/packages/94/6d/344a164d32d65d503ffe9201cd74cf13a020099dc446554d1e50b07f167b/libusb1-3.3.1-py3-none-win_amd64.whl", hash = "sha256:6e21b772d80d6487fbb55d3d2141218536db302da82f1983754e96c72781c102", size = 141080, upload_time = "2025-03-24T05:36:46.594Z" }, ] [[package]] name = "llvmlite" version = "0.44.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880 } +sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880, upload_time = "2025-01-20T11:14:41.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", size = 28132305 }, - { url = "https://files.pythonhosted.org/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", size = 26201090 }, - { url = "https://files.pythonhosted.org/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", size = 42361858 }, - { url = "https://files.pythonhosted.org/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610", size = 41184200 }, - { url = "https://files.pythonhosted.org/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", size = 30331193 }, - { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297 }, - { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105 }, - { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901 }, - { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247 }, - { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380 }, + { url = "https://files.pythonhosted.org/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", size = 28132305, upload_time = "2025-01-20T11:12:53.936Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", size = 26201090, upload_time = "2025-01-20T11:12:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", size = 42361858, upload_time = "2025-01-20T11:13:07.623Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610", size = 41184200, upload_time = "2025-01-20T11:13:20.058Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", size = 30331193, upload_time = "2025-01-20T11:13:26.976Z" }, + { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297, upload_time = "2025-01-20T11:13:32.57Z" }, + { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105, upload_time = "2025-01-20T11:13:38.744Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901, upload_time = "2025-01-20T11:13:46.711Z" }, + { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247, upload_time = "2025-01-20T11:13:56.159Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380, upload_time = "2025-01-20T11:14:02.442Z" }, ] [[package]] name = "lru-dict" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/e3/42c87871920602a3c8300915bd0292f76eccc66c38f782397acbf8a62088/lru-dict-1.3.0.tar.gz", hash = "sha256:54fd1966d6bd1fcde781596cb86068214edeebff1db13a2cea11079e3fd07b6b", size = 13123 } +sdist = { url = "https://files.pythonhosted.org/packages/96/e3/42c87871920602a3c8300915bd0292f76eccc66c38f782397acbf8a62088/lru-dict-1.3.0.tar.gz", hash = "sha256:54fd1966d6bd1fcde781596cb86068214edeebff1db13a2cea11079e3fd07b6b", size = 13123, upload_time = "2023-11-06T01:40:12.951Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/c9/6fac0cb67160f0efa3cc76a6a7d04d5e21a516eeb991ebba08f4f8f01ec5/lru_dict-1.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:20c595764695d20bdc3ab9b582e0cc99814da183544afb83783a36d6741a0dac", size = 17750 }, - { url = "https://files.pythonhosted.org/packages/61/14/f90dee4bc547ae266dbeffd4e11611234bb6af511dea48f3bc8dac1de478/lru_dict-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d9b30a8f50c3fa72a494eca6be5810a1b5c89e4f0fda89374f0d1c5ad8d37d51", size = 11055 }, - { url = "https://files.pythonhosted.org/packages/4e/63/a0ae20525f9d52f62ac0def47935f8a2b3b6fcd2c145218b9a27fc1fb910/lru_dict-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9710737584650a4251b9a566cbb1a86f83437adb209c9ba43a4e756d12faf0d7", size = 11330 }, - { url = "https://files.pythonhosted.org/packages/e9/c6/8c2b81b61e5206910c81b712500736227289aefe4ccfb36137aa21807003/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b84c321ae34f2f40aae80e18b6fa08b31c90095792ab64bb99d2e385143effaa", size = 31793 }, - { url = "https://files.pythonhosted.org/packages/f9/d7/af9733f94df67a2e9e31ef47d4c41aff1836024f135cdbda4743eb628452/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eed24272b4121b7c22f234daed99899817d81d671b3ed030c876ac88bc9dc890", size = 33090 }, - { url = "https://files.pythonhosted.org/packages/5b/6e/5b09b069a70028bcf05dbdc57a301fbe8b3bafecf916f2ed5a3065c79a71/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd13af06dab7c6ee92284fd02ed9a5613a07d5c1b41948dc8886e7207f86dfd", size = 29795 }, - { url = "https://files.pythonhosted.org/packages/21/92/4690daefc2602f7c3429ecf54572d37a9e3c372d370344d2185daa4d5ecc/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1efc59bfba6aac33684d87b9e02813b0e2445b2f1c444dae2a0b396ad0ed60c", size = 31586 }, - { url = "https://files.pythonhosted.org/packages/3c/67/0a29a91087196b02f278d8765120ee4e7486f1f72a4c505fd1cd3109e627/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfaf75ac574447afcf8ad998789071af11d2bcf6f947643231f692948839bd98", size = 36662 }, - { url = "https://files.pythonhosted.org/packages/36/54/8d56c514cd2333b652bd44c8f1962ab986cbe68e8ad7258c9e0f360cddb6/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c95f8751e2abd6f778da0399c8e0239321d560dbc58cb063827123137d213242", size = 35118 }, - { url = "https://files.pythonhosted.org/packages/f5/9a/c7a175d10d503b86974cb07141ca175947145dd1c7370fcda86fbbcaf326/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:abd0c284b26b5c4ee806ca4f33ab5e16b4bf4d5ec9e093e75a6f6287acdde78e", size = 38198 }, - { url = "https://files.pythonhosted.org/packages/fd/59/2e5086c8e8a05a7282a824a2a37e3c45cd5714e7b83d8bc0267cb3bb5b4f/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a47740652b25900ac5ce52667b2eade28d8b5fdca0ccd3323459df710e8210a", size = 36542 }, - { url = "https://files.pythonhosted.org/packages/12/52/80d0a06e5f45fe7c278dd662da6ea5b39f2ff003248f448189932f6b71c2/lru_dict-1.3.0-cp311-cp311-win32.whl", hash = "sha256:a690c23fc353681ed8042d9fe8f48f0fb79a57b9a45daea2f0be1eef8a1a4aa4", size = 12533 }, - { url = "https://files.pythonhosted.org/packages/ce/fe/1f12f33513310860ec6d722709ec4ad8256d9dcc3385f6ae2a244e6e66f5/lru_dict-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:efd3f4e0385d18f20f7ea6b08af2574c1bfaa5cb590102ef1bee781bdfba84bc", size = 13651 }, - { url = "https://files.pythonhosted.org/packages/fc/5c/385f080747eb3083af87d8e4c9068f3c4cab89035f6982134889940dafd8/lru_dict-1.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c279068f68af3b46a5d649855e1fb87f5705fe1f744a529d82b2885c0e1fc69d", size = 17174 }, - { url = "https://files.pythonhosted.org/packages/3c/de/5ef2ed75ce55d7059d1b96177ba04fa7ee1f35564f97bdfcd28fccfbe9d2/lru_dict-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:350e2233cfee9f326a0d7a08e309372d87186565e43a691b120006285a0ac549", size = 10742 }, - { url = "https://files.pythonhosted.org/packages/ca/05/f69a6abb0062d2cf2ce0aaf0284b105b97d1da024ca6d3d0730e6151242e/lru_dict-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4eafb188a84483b3231259bf19030859f070321b00326dcb8e8c6cbf7db4b12f", size = 11079 }, - { url = "https://files.pythonhosted.org/packages/ea/59/cf891143abe58a455b8eaa9175f0e80f624a146a2bf9a1ca842ee0ef930a/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73593791047e36b37fdc0b67b76aeed439fcea80959c7d46201240f9ec3b2563", size = 32469 }, - { url = "https://files.pythonhosted.org/packages/59/88/d5976e9f70107ce11e45d93c6f0c2d5eaa1fc30bb3c8f57525eda4510dff/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1958cb70b9542773d6241974646e5410e41ef32e5c9e437d44040d59bd80daf2", size = 33496 }, - { url = "https://files.pythonhosted.org/packages/6c/f8/94d6e910d54fc1fa05c0ee1cd608c39401866a18cf5e5aff238449b33c11/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1cd3ed2cee78a47f11f3b70be053903bda197a873fd146e25c60c8e5a32cd6", size = 29914 }, - { url = "https://files.pythonhosted.org/packages/ca/b9/9db79780c8a3cfd66bba6847773061e5cf8a3746950273b9985d47bbfe53/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82eb230d48eaebd6977a92ddaa6d788f14cf4f4bcf5bbffa4ddfd60d051aa9d4", size = 32241 }, - { url = "https://files.pythonhosted.org/packages/9b/b6/08a623019daec22a40c4d6d2c40851dfa3d129a53b2f9469db8eb13666c1/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5ad659cbc349d0c9ba8e536b5f40f96a70c360f43323c29f4257f340d891531c", size = 37320 }, - { url = "https://files.pythonhosted.org/packages/70/0b/d3717159c26155ff77679cee1b077d22e1008bf45f19921e193319cd8e46/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba490b8972531d153ac0d4e421f60d793d71a2f4adbe2f7740b3c55dce0a12f1", size = 35054 }, - { url = "https://files.pythonhosted.org/packages/04/74/f2ae00de7c27984a19b88d2b09ac877031c525b01199d7841ec8fa657fd6/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:c0131351b8a7226c69f1eba5814cbc9d1d8daaf0fdec1ae3f30508e3de5262d4", size = 38613 }, - { url = "https://files.pythonhosted.org/packages/5a/0b/e30236aafe31b4247aa9ae61ba8aac6dde75c3ea0e47a8fb7eef53f6d5ce/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e88dba16695f17f41701269fa046197a3fd7b34a8dba744c8749303ddaa18df", size = 37143 }, - { url = "https://files.pythonhosted.org/packages/1c/28/b59bcebb8d76ba8147a784a8be7eab6a4ad3395b9236e73740ff675a5a52/lru_dict-1.3.0-cp312-cp312-win32.whl", hash = "sha256:6ffaf595e625b388babc8e7d79b40f26c7485f61f16efe76764e32dce9ea17fc", size = 12653 }, - { url = "https://files.pythonhosted.org/packages/bd/18/06d9710cb0a0d3634f8501e4bdcc07abe64a32e404d82895a6a36fab97f6/lru_dict-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf9da32ef2582434842ab6ba6e67290debfae72771255a8e8ab16f3e006de0aa", size = 13811 }, + { url = "https://files.pythonhosted.org/packages/a8/c9/6fac0cb67160f0efa3cc76a6a7d04d5e21a516eeb991ebba08f4f8f01ec5/lru_dict-1.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:20c595764695d20bdc3ab9b582e0cc99814da183544afb83783a36d6741a0dac", size = 17750, upload_time = "2023-11-06T01:38:52.667Z" }, + { url = "https://files.pythonhosted.org/packages/61/14/f90dee4bc547ae266dbeffd4e11611234bb6af511dea48f3bc8dac1de478/lru_dict-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d9b30a8f50c3fa72a494eca6be5810a1b5c89e4f0fda89374f0d1c5ad8d37d51", size = 11055, upload_time = "2023-11-06T01:38:53.798Z" }, + { url = "https://files.pythonhosted.org/packages/4e/63/a0ae20525f9d52f62ac0def47935f8a2b3b6fcd2c145218b9a27fc1fb910/lru_dict-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9710737584650a4251b9a566cbb1a86f83437adb209c9ba43a4e756d12faf0d7", size = 11330, upload_time = "2023-11-06T01:38:54.847Z" }, + { url = "https://files.pythonhosted.org/packages/e9/c6/8c2b81b61e5206910c81b712500736227289aefe4ccfb36137aa21807003/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b84c321ae34f2f40aae80e18b6fa08b31c90095792ab64bb99d2e385143effaa", size = 31793, upload_time = "2023-11-06T01:38:56.163Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d7/af9733f94df67a2e9e31ef47d4c41aff1836024f135cdbda4743eb628452/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eed24272b4121b7c22f234daed99899817d81d671b3ed030c876ac88bc9dc890", size = 33090, upload_time = "2023-11-06T01:38:57.091Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6e/5b09b069a70028bcf05dbdc57a301fbe8b3bafecf916f2ed5a3065c79a71/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd13af06dab7c6ee92284fd02ed9a5613a07d5c1b41948dc8886e7207f86dfd", size = 29795, upload_time = "2023-11-06T01:38:58.278Z" }, + { url = "https://files.pythonhosted.org/packages/21/92/4690daefc2602f7c3429ecf54572d37a9e3c372d370344d2185daa4d5ecc/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1efc59bfba6aac33684d87b9e02813b0e2445b2f1c444dae2a0b396ad0ed60c", size = 31586, upload_time = "2023-11-06T01:38:59.363Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/0a29a91087196b02f278d8765120ee4e7486f1f72a4c505fd1cd3109e627/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfaf75ac574447afcf8ad998789071af11d2bcf6f947643231f692948839bd98", size = 36662, upload_time = "2023-11-06T01:39:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/8d56c514cd2333b652bd44c8f1962ab986cbe68e8ad7258c9e0f360cddb6/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c95f8751e2abd6f778da0399c8e0239321d560dbc58cb063827123137d213242", size = 35118, upload_time = "2023-11-06T01:39:01.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/9a/c7a175d10d503b86974cb07141ca175947145dd1c7370fcda86fbbcaf326/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:abd0c284b26b5c4ee806ca4f33ab5e16b4bf4d5ec9e093e75a6f6287acdde78e", size = 38198, upload_time = "2023-11-06T01:39:03.306Z" }, + { url = "https://files.pythonhosted.org/packages/fd/59/2e5086c8e8a05a7282a824a2a37e3c45cd5714e7b83d8bc0267cb3bb5b4f/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a47740652b25900ac5ce52667b2eade28d8b5fdca0ccd3323459df710e8210a", size = 36542, upload_time = "2023-11-06T01:39:04.751Z" }, + { url = "https://files.pythonhosted.org/packages/12/52/80d0a06e5f45fe7c278dd662da6ea5b39f2ff003248f448189932f6b71c2/lru_dict-1.3.0-cp311-cp311-win32.whl", hash = "sha256:a690c23fc353681ed8042d9fe8f48f0fb79a57b9a45daea2f0be1eef8a1a4aa4", size = 12533, upload_time = "2023-11-06T01:39:05.838Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fe/1f12f33513310860ec6d722709ec4ad8256d9dcc3385f6ae2a244e6e66f5/lru_dict-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:efd3f4e0385d18f20f7ea6b08af2574c1bfaa5cb590102ef1bee781bdfba84bc", size = 13651, upload_time = "2023-11-06T01:39:06.871Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5c/385f080747eb3083af87d8e4c9068f3c4cab89035f6982134889940dafd8/lru_dict-1.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c279068f68af3b46a5d649855e1fb87f5705fe1f744a529d82b2885c0e1fc69d", size = 17174, upload_time = "2023-11-06T01:39:07.923Z" }, + { url = "https://files.pythonhosted.org/packages/3c/de/5ef2ed75ce55d7059d1b96177ba04fa7ee1f35564f97bdfcd28fccfbe9d2/lru_dict-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:350e2233cfee9f326a0d7a08e309372d87186565e43a691b120006285a0ac549", size = 10742, upload_time = "2023-11-06T01:39:08.871Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/f69a6abb0062d2cf2ce0aaf0284b105b97d1da024ca6d3d0730e6151242e/lru_dict-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4eafb188a84483b3231259bf19030859f070321b00326dcb8e8c6cbf7db4b12f", size = 11079, upload_time = "2023-11-06T01:39:09.766Z" }, + { url = "https://files.pythonhosted.org/packages/ea/59/cf891143abe58a455b8eaa9175f0e80f624a146a2bf9a1ca842ee0ef930a/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73593791047e36b37fdc0b67b76aeed439fcea80959c7d46201240f9ec3b2563", size = 32469, upload_time = "2023-11-06T01:39:11.091Z" }, + { url = "https://files.pythonhosted.org/packages/59/88/d5976e9f70107ce11e45d93c6f0c2d5eaa1fc30bb3c8f57525eda4510dff/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1958cb70b9542773d6241974646e5410e41ef32e5c9e437d44040d59bd80daf2", size = 33496, upload_time = "2023-11-06T01:39:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/94d6e910d54fc1fa05c0ee1cd608c39401866a18cf5e5aff238449b33c11/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1cd3ed2cee78a47f11f3b70be053903bda197a873fd146e25c60c8e5a32cd6", size = 29914, upload_time = "2023-11-06T01:39:13.395Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b9/9db79780c8a3cfd66bba6847773061e5cf8a3746950273b9985d47bbfe53/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82eb230d48eaebd6977a92ddaa6d788f14cf4f4bcf5bbffa4ddfd60d051aa9d4", size = 32241, upload_time = "2023-11-06T01:39:14.612Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b6/08a623019daec22a40c4d6d2c40851dfa3d129a53b2f9469db8eb13666c1/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5ad659cbc349d0c9ba8e536b5f40f96a70c360f43323c29f4257f340d891531c", size = 37320, upload_time = "2023-11-06T01:39:15.875Z" }, + { url = "https://files.pythonhosted.org/packages/70/0b/d3717159c26155ff77679cee1b077d22e1008bf45f19921e193319cd8e46/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba490b8972531d153ac0d4e421f60d793d71a2f4adbe2f7740b3c55dce0a12f1", size = 35054, upload_time = "2023-11-06T01:39:17.063Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f2ae00de7c27984a19b88d2b09ac877031c525b01199d7841ec8fa657fd6/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:c0131351b8a7226c69f1eba5814cbc9d1d8daaf0fdec1ae3f30508e3de5262d4", size = 38613, upload_time = "2023-11-06T01:39:18.136Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0b/e30236aafe31b4247aa9ae61ba8aac6dde75c3ea0e47a8fb7eef53f6d5ce/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e88dba16695f17f41701269fa046197a3fd7b34a8dba744c8749303ddaa18df", size = 37143, upload_time = "2023-11-06T01:39:19.571Z" }, + { url = "https://files.pythonhosted.org/packages/1c/28/b59bcebb8d76ba8147a784a8be7eab6a4ad3395b9236e73740ff675a5a52/lru_dict-1.3.0-cp312-cp312-win32.whl", hash = "sha256:6ffaf595e625b388babc8e7d79b40f26c7485f61f16efe76764e32dce9ea17fc", size = 12653, upload_time = "2023-11-06T01:39:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/bd/18/06d9710cb0a0d3634f8501e4bdcc07abe64a32e404d82895a6a36fab97f6/lru_dict-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf9da32ef2582434842ab6ba6e67290debfae72771255a8e8ab16f3e006de0aa", size = 13811, upload_time = "2023-11-06T01:39:21.599Z" }, ] [[package]] name = "lxml" -version = "5.3.2" +version = "5.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/61/d3dc048cd6c7be6fe45b80cedcbdd4326ba4d550375f266d9f4246d0f4bc/lxml-5.3.2.tar.gz", hash = "sha256:773947d0ed809ddad824b7b14467e1a481b8976e87278ac4a730c2f7c7fcddc1", size = 3679948 } +sdist = { url = "https://files.pythonhosted.org/packages/76/3d/14e82fc7c8fb1b7761f7e748fd47e2ec8276d137b6acfe5a4bb73853e08f/lxml-5.4.0.tar.gz", hash = "sha256:d12832e1dbea4be280b22fd0ea7c9b87f0d8fc51ba06e92dc62d52f804f78ebd", size = 3679479, upload_time = "2025-04-23T01:50:29.322Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/b8/2b727f5a90902f7cc5548349f563b60911ca05f3b92e35dfa751349f265f/lxml-5.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d61a7d0d208ace43986a92b111e035881c4ed45b1f5b7a270070acae8b0bfb4", size = 8163457 }, - { url = "https://files.pythonhosted.org/packages/91/84/23135b2dc72b3440d68c8f39ace2bb00fe78e3a2255f7c74f7e76f22498e/lxml-5.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856dfd7eda0b75c29ac80a31a6411ca12209183e866c33faf46e77ace3ce8a79", size = 4433445 }, - { url = "https://files.pythonhosted.org/packages/c9/1c/6900ade2294488f80598af7b3229669562166384bb10bf4c915342a2f288/lxml-5.3.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a01679e4aad0727bedd4c9407d4d65978e920f0200107ceeffd4b019bd48529", size = 5029603 }, - { url = "https://files.pythonhosted.org/packages/2f/e9/31dbe5deaccf0d33ec279cf400306ad4b32dfd1a0fee1fca40c5e90678fe/lxml-5.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6b37b4c3acb8472d191816d4582379f64d81cecbdce1a668601745c963ca5cc", size = 4771236 }, - { url = "https://files.pythonhosted.org/packages/68/41/c3412392884130af3415af2e89a2007e00b2a782be6fb848a95b598a114c/lxml-5.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3df5a54e7b7c31755383f126d3a84e12a4e0333db4679462ef1165d702517477", size = 5369815 }, - { url = "https://files.pythonhosted.org/packages/34/0a/ba0309fd5f990ea0cc05aba2bea225ef1bcb07ecbf6c323c6b119fc46e7f/lxml-5.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c09a40f28dcded933dc16217d6a092be0cc49ae25811d3b8e937c8060647c353", size = 4843663 }, - { url = "https://files.pythonhosted.org/packages/b6/c6/663b5d87d51d00d4386a2d52742a62daa486c5dc6872a443409d9aeafece/lxml-5.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1ef20f1851ccfbe6c5a04c67ec1ce49da16ba993fdbabdce87a92926e505412", size = 4918028 }, - { url = "https://files.pythonhosted.org/packages/75/5f/f6a72ccbe05cf83341d4b6ad162ed9e1f1ffbd12f1c4b8bc8ae413392282/lxml-5.3.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f79a63289dbaba964eb29ed3c103b7911f2dce28c36fe87c36a114e6bd21d7ad", size = 4792005 }, - { url = "https://files.pythonhosted.org/packages/37/7b/8abd5b332252239ffd28df5842ee4e5bf56e1c613c323586c21ccf5af634/lxml-5.3.2-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:75a72697d95f27ae00e75086aed629f117e816387b74a2f2da6ef382b460b710", size = 5405363 }, - { url = "https://files.pythonhosted.org/packages/5a/79/549b7ec92b8d9feb13869c1b385a0749d7ccfe5590d1e60f11add9cdd580/lxml-5.3.2-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:b9b00c9ee1cc3a76f1f16e94a23c344e0b6e5c10bec7f94cf2d820ce303b8c01", size = 4932915 }, - { url = "https://files.pythonhosted.org/packages/57/eb/4fa626d0bac8b4f2aa1d0e6a86232db030fd0f462386daf339e4a0ee352b/lxml-5.3.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:77cbcab50cbe8c857c6ba5f37f9a3976499c60eada1bf6d38f88311373d7b4bc", size = 4983473 }, - { url = "https://files.pythonhosted.org/packages/1b/c8/79d61d13cbb361c2c45fbe7c8bd00ea6a23b3e64bc506264d2856c60d702/lxml-5.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29424058f072a24622a0a15357bca63d796954758248a72da6d512f9bd9a4493", size = 4855284 }, - { url = "https://files.pythonhosted.org/packages/80/16/9f84e1ef03a13136ab4f9482c9adaaad425c68b47556b9d3192a782e5d37/lxml-5.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7d82737a8afe69a7c80ef31d7626075cc7d6e2267f16bf68af2c764b45ed68ab", size = 5458355 }, - { url = "https://files.pythonhosted.org/packages/aa/6d/f62860451bb4683e87636e49effb76d499773337928e53356c1712ccec24/lxml-5.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:95473d1d50a5d9fcdb9321fdc0ca6e1edc164dce4c7da13616247d27f3d21e31", size = 5300051 }, - { url = "https://files.pythonhosted.org/packages/3f/5f/3b6c4acec17f9a57ea8bb89a658a70621db3fb86ea588e7703b6819d9b03/lxml-5.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2162068f6da83613f8b2a32ca105e37a564afd0d7009b0b25834d47693ce3538", size = 5033481 }, - { url = "https://files.pythonhosted.org/packages/79/bd/3c4dd7d903bb9981f4876c61ef2ff5d5473e409ef61dc7337ac207b91920/lxml-5.3.2-cp311-cp311-win32.whl", hash = "sha256:f8695752cf5d639b4e981afe6c99e060621362c416058effd5c704bede9cb5d1", size = 3474266 }, - { url = "https://files.pythonhosted.org/packages/1f/ea/9311fa1ef75b7d601c89600fc612838ee77ad3d426184941cba9cf62641f/lxml-5.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:d1a94cbb4ee64af3ab386c2d63d6d9e9cf2e256ac0fd30f33ef0a3c88f575174", size = 3815230 }, - { url = "https://files.pythonhosted.org/packages/0d/7e/c749257a7fabc712c4df57927b0f703507f316e9f2c7e3219f8f76d36145/lxml-5.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:16b3897691ec0316a1aa3c6585f61c8b7978475587c5b16fc1d2c28d283dc1b0", size = 8193212 }, - { url = "https://files.pythonhosted.org/packages/a8/50/17e985ba162c9f1ca119f4445004b58f9e5ef559ded599b16755e9bfa260/lxml-5.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8d4b34a0eeaf6e73169dcfd653c8d47f25f09d806c010daf074fba2db5e2d3f", size = 4451439 }, - { url = "https://files.pythonhosted.org/packages/c2/b5/4960ba0fcca6ce394ed4a2f89ee13083e7fcbe9641a91166e8e9792fedb1/lxml-5.3.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cd7a959396da425022e1e4214895b5cfe7de7035a043bcc2d11303792b67554", size = 5052146 }, - { url = "https://files.pythonhosted.org/packages/5f/d1/184b04481a5d1f5758916de087430752a7b229bddbd6c1d23405078c72bd/lxml-5.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cac5eaeec3549c5df7f8f97a5a6db6963b91639389cdd735d5a806370847732b", size = 4789082 }, - { url = "https://files.pythonhosted.org/packages/7d/75/1a19749d373e9a3d08861addccdf50c92b628c67074b22b8f3c61997cf5a/lxml-5.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b5f7d77334877c2146e7bb8b94e4df980325fab0a8af4d524e5d43cd6f789d", size = 5312300 }, - { url = "https://files.pythonhosted.org/packages/fb/00/9d165d4060d3f347e63b219fcea5c6a3f9193e9e2868c6801e18e5379725/lxml-5.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13f3495cfec24e3d63fffd342cc8141355d1d26ee766ad388775f5c8c5ec3932", size = 4836655 }, - { url = "https://files.pythonhosted.org/packages/b8/e9/06720a33cc155966448a19677f079100517b6629a872382d22ebd25e48aa/lxml-5.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e70ad4c9658beeff99856926fd3ee5fde8b519b92c693f856007177c36eb2e30", size = 4961795 }, - { url = "https://files.pythonhosted.org/packages/2d/57/4540efab2673de2904746b37ef7f74385329afd4643ed92abcc9ec6e00ca/lxml-5.3.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:507085365783abd7879fa0a6fa55eddf4bdd06591b17a2418403bb3aff8a267d", size = 4779791 }, - { url = "https://files.pythonhosted.org/packages/99/ad/6056edf6c9f4fa1d41e6fbdae52c733a4a257fd0d7feccfa26ae051bb46f/lxml-5.3.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:5bb304f67cbf5dfa07edad904732782cbf693286b9cd85af27059c5779131050", size = 5346807 }, - { url = "https://files.pythonhosted.org/packages/a1/fa/5be91fc91a18f3f705ea5533bc2210b25d738c6b615bf1c91e71a9b2f26b/lxml-5.3.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:3d84f5c093645c21c29a4e972b84cb7cf682f707f8706484a5a0c7ff13d7a988", size = 4909213 }, - { url = "https://files.pythonhosted.org/packages/f3/74/71bb96a3b5ae36b74e0402f4fa319df5559a8538577f8c57c50f1b57dc15/lxml-5.3.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:bdc13911db524bd63f37b0103af014b7161427ada41f1b0b3c9b5b5a9c1ca927", size = 4987694 }, - { url = "https://files.pythonhosted.org/packages/08/c2/3953a68b0861b2f97234b1838769269478ccf872d8ea7a26e911238220ad/lxml-5.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ec944539543f66ebc060ae180d47e86aca0188bda9cbfadff47d86b0dc057dc", size = 4862865 }, - { url = "https://files.pythonhosted.org/packages/e0/9a/52e48f7cfd5a5e61f44a77e679880580dfb4f077af52d6ed5dd97e3356fe/lxml-5.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:59d437cc8a7f838282df5a199cf26f97ef08f1c0fbec6e84bd6f5cc2b7913f6e", size = 5423383 }, - { url = "https://files.pythonhosted.org/packages/17/67/42fe1d489e4dcc0b264bef361aef0b929fbb2b5378702471a3043bc6982c/lxml-5.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e275961adbd32e15672e14e0cc976a982075208224ce06d149c92cb43db5b93", size = 5286864 }, - { url = "https://files.pythonhosted.org/packages/29/e4/03b1d040ee3aaf2bd4e1c2061de2eae1178fe9a460d3efc1ea7ef66f6011/lxml-5.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:038aeb6937aa404480c2966b7f26f1440a14005cb0702078c173c028eca72c31", size = 5056819 }, - { url = "https://files.pythonhosted.org/packages/83/b3/e2ec8a6378e4d87da3af9de7c862bcea7ca624fc1a74b794180c82e30123/lxml-5.3.2-cp312-cp312-win32.whl", hash = "sha256:3c2c8d0fa3277147bff180e3590be67597e17d365ce94beb2efa3138a2131f71", size = 3486177 }, - { url = "https://files.pythonhosted.org/packages/d5/8a/6a08254b0bab2da9573735725caab8302a2a1c9b3818533b41568ca489be/lxml-5.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:77809fcd97dfda3f399102db1794f7280737b69830cd5c961ac87b3c5c05662d", size = 3817134 }, + { url = "https://files.pythonhosted.org/packages/81/2d/67693cc8a605a12e5975380d7ff83020dcc759351b5a066e1cced04f797b/lxml-5.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98a3912194c079ef37e716ed228ae0dcb960992100461b704aea4e93af6b0bb9", size = 8083240, upload_time = "2025-04-23T01:45:18.566Z" }, + { url = "https://files.pythonhosted.org/packages/73/53/b5a05ab300a808b72e848efd152fe9c022c0181b0a70b8bca1199f1bed26/lxml-5.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ea0252b51d296a75f6118ed0d8696888e7403408ad42345d7dfd0d1e93309a7", size = 4387685, upload_time = "2025-04-23T01:45:21.387Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/1a3879c5f512bdcd32995c301886fe082b2edd83c87d41b6d42d89b4ea4d/lxml-5.4.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92b69441d1bd39f4940f9eadfa417a25862242ca2c396b406f9272ef09cdcaa", size = 4991164, upload_time = "2025-04-23T01:45:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/f9/94/bbc66e42559f9d04857071e3b3d0c9abd88579367fd2588a4042f641f57e/lxml-5.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20e16c08254b9b6466526bc1828d9370ee6c0d60a4b64836bc3ac2917d1e16df", size = 4746206, upload_time = "2025-04-23T01:45:26.361Z" }, + { url = "https://files.pythonhosted.org/packages/66/95/34b0679bee435da2d7cae895731700e519a8dfcab499c21662ebe671603e/lxml-5.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7605c1c32c3d6e8c990dd28a0970a3cbbf1429d5b92279e37fda05fb0c92190e", size = 5342144, upload_time = "2025-04-23T01:45:28.939Z" }, + { url = "https://files.pythonhosted.org/packages/e0/5d/abfcc6ab2fa0be72b2ba938abdae1f7cad4c632f8d552683ea295d55adfb/lxml-5.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecf4c4b83f1ab3d5a7ace10bafcb6f11df6156857a3c418244cef41ca9fa3e44", size = 4825124, upload_time = "2025-04-23T01:45:31.361Z" }, + { url = "https://files.pythonhosted.org/packages/5a/78/6bd33186c8863b36e084f294fc0a5e5eefe77af95f0663ef33809cc1c8aa/lxml-5.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cef4feae82709eed352cd7e97ae062ef6ae9c7b5dbe3663f104cd2c0e8d94ba", size = 4876520, upload_time = "2025-04-23T01:45:34.191Z" }, + { url = "https://files.pythonhosted.org/packages/3b/74/4d7ad4839bd0fc64e3d12da74fc9a193febb0fae0ba6ebd5149d4c23176a/lxml-5.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:df53330a3bff250f10472ce96a9af28628ff1f4efc51ccba351a8820bca2a8ba", size = 4765016, upload_time = "2025-04-23T01:45:36.7Z" }, + { url = "https://files.pythonhosted.org/packages/24/0d/0a98ed1f2471911dadfc541003ac6dd6879fc87b15e1143743ca20f3e973/lxml-5.4.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:aefe1a7cb852fa61150fcb21a8c8fcea7b58c4cb11fbe59c97a0a4b31cae3c8c", size = 5362884, upload_time = "2025-04-23T01:45:39.291Z" }, + { url = "https://files.pythonhosted.org/packages/48/de/d4f7e4c39740a6610f0f6959052b547478107967362e8424e1163ec37ae8/lxml-5.4.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ef5a7178fcc73b7d8c07229e89f8eb45b2908a9238eb90dcfc46571ccf0383b8", size = 4902690, upload_time = "2025-04-23T01:45:42.386Z" }, + { url = "https://files.pythonhosted.org/packages/07/8c/61763abd242af84f355ca4ef1ee096d3c1b7514819564cce70fd18c22e9a/lxml-5.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d2ed1b3cb9ff1c10e6e8b00941bb2e5bb568b307bfc6b17dffbbe8be5eecba86", size = 4944418, upload_time = "2025-04-23T01:45:46.051Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/6d7e3b63e7e282619193961a570c0a4c8a57fe820f07ca3fe2f6bd86608a/lxml-5.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:72ac9762a9f8ce74c9eed4a4e74306f2f18613a6b71fa065495a67ac227b3056", size = 4827092, upload_time = "2025-04-23T01:45:48.943Z" }, + { url = "https://files.pythonhosted.org/packages/71/4a/e60a306df54680b103348545706a98a7514a42c8b4fbfdcaa608567bb065/lxml-5.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f5cb182f6396706dc6cc1896dd02b1c889d644c081b0cdec38747573db88a7d7", size = 5418231, upload_time = "2025-04-23T01:45:51.481Z" }, + { url = "https://files.pythonhosted.org/packages/27/f2/9754aacd6016c930875854f08ac4b192a47fe19565f776a64004aa167521/lxml-5.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3a3178b4873df8ef9457a4875703488eb1622632a9cee6d76464b60e90adbfcd", size = 5261798, upload_time = "2025-04-23T01:45:54.146Z" }, + { url = "https://files.pythonhosted.org/packages/38/a2/0c49ec6941428b1bd4f280650d7b11a0f91ace9db7de32eb7aa23bcb39ff/lxml-5.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e094ec83694b59d263802ed03a8384594fcce477ce484b0cbcd0008a211ca751", size = 4988195, upload_time = "2025-04-23T01:45:56.685Z" }, + { url = "https://files.pythonhosted.org/packages/7a/75/87a3963a08eafc46a86c1131c6e28a4de103ba30b5ae903114177352a3d7/lxml-5.4.0-cp311-cp311-win32.whl", hash = "sha256:4329422de653cdb2b72afa39b0aa04252fca9071550044904b2e7036d9d97fe4", size = 3474243, upload_time = "2025-04-23T01:45:58.863Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/1f0964c4f6c2be861c50db380c554fb8befbea98c6404744ce243a3c87ef/lxml-5.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd3be6481ef54b8cfd0e1e953323b7aa9d9789b94842d0e5b142ef4bb7999539", size = 3815197, upload_time = "2025-04-23T01:46:01.096Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4c/d101ace719ca6a4ec043eb516fcfcb1b396a9fccc4fcd9ef593df34ba0d5/lxml-5.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5aff6f3e818e6bdbbb38e5967520f174b18f539c2b9de867b1e7fde6f8d95a4", size = 8127392, upload_time = "2025-04-23T01:46:04.09Z" }, + { url = "https://files.pythonhosted.org/packages/11/84/beddae0cec4dd9ddf46abf156f0af451c13019a0fa25d7445b655ba5ccb7/lxml-5.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942a5d73f739ad7c452bf739a62a0f83e2578afd6b8e5406308731f4ce78b16d", size = 4415103, upload_time = "2025-04-23T01:46:07.227Z" }, + { url = "https://files.pythonhosted.org/packages/d0/25/d0d93a4e763f0462cccd2b8a665bf1e4343dd788c76dcfefa289d46a38a9/lxml-5.4.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460508a4b07364d6abf53acaa0a90b6d370fafde5693ef37602566613a9b0779", size = 5024224, upload_time = "2025-04-23T01:46:10.237Z" }, + { url = "https://files.pythonhosted.org/packages/31/ce/1df18fb8f7946e7f3388af378b1f34fcf253b94b9feedb2cec5969da8012/lxml-5.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529024ab3a505fed78fe3cc5ddc079464e709f6c892733e3f5842007cec8ac6e", size = 4769913, upload_time = "2025-04-23T01:46:12.757Z" }, + { url = "https://files.pythonhosted.org/packages/4e/62/f4a6c60ae7c40d43657f552f3045df05118636be1165b906d3423790447f/lxml-5.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ca56ebc2c474e8f3d5761debfd9283b8b18c76c4fc0967b74aeafba1f5647f9", size = 5290441, upload_time = "2025-04-23T01:46:16.037Z" }, + { url = "https://files.pythonhosted.org/packages/9e/aa/04f00009e1e3a77838c7fc948f161b5d2d5de1136b2b81c712a263829ea4/lxml-5.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a81e1196f0a5b4167a8dafe3a66aa67c4addac1b22dc47947abd5d5c7a3f24b5", size = 4820165, upload_time = "2025-04-23T01:46:19.137Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/e0b2f61fa2404bf0f1fdf1898377e5bd1b74cc9b2cf2c6ba8509b8f27990/lxml-5.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b8686694423ddae324cf614e1b9659c2edb754de617703c3d29ff568448df5", size = 4932580, upload_time = "2025-04-23T01:46:21.963Z" }, + { url = "https://files.pythonhosted.org/packages/24/a2/8263f351b4ffe0ed3e32ea7b7830f845c795349034f912f490180d88a877/lxml-5.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c5681160758d3f6ac5b4fea370495c48aac0989d6a0f01bb9a72ad8ef5ab75c4", size = 4759493, upload_time = "2025-04-23T01:46:24.316Z" }, + { url = "https://files.pythonhosted.org/packages/05/00/41db052f279995c0e35c79d0f0fc9f8122d5b5e9630139c592a0b58c71b4/lxml-5.4.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:2dc191e60425ad70e75a68c9fd90ab284df64d9cd410ba8d2b641c0c45bc006e", size = 5324679, upload_time = "2025-04-23T01:46:27.097Z" }, + { url = "https://files.pythonhosted.org/packages/1d/be/ee99e6314cdef4587617d3b3b745f9356d9b7dd12a9663c5f3b5734b64ba/lxml-5.4.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:67f779374c6b9753ae0a0195a892a1c234ce8416e4448fe1e9f34746482070a7", size = 4890691, upload_time = "2025-04-23T01:46:30.009Z" }, + { url = "https://files.pythonhosted.org/packages/ad/36/239820114bf1d71f38f12208b9c58dec033cbcf80101cde006b9bde5cffd/lxml-5.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:79d5bfa9c1b455336f52343130b2067164040604e41f6dc4d8313867ed540079", size = 4955075, upload_time = "2025-04-23T01:46:32.33Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e1/1b795cc0b174efc9e13dbd078a9ff79a58728a033142bc6d70a1ee8fc34d/lxml-5.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d3c30ba1c9b48c68489dc1829a6eede9873f52edca1dda900066542528d6b20", size = 4838680, upload_time = "2025-04-23T01:46:34.852Z" }, + { url = "https://files.pythonhosted.org/packages/72/48/3c198455ca108cec5ae3662ae8acd7fd99476812fd712bb17f1b39a0b589/lxml-5.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1af80c6316ae68aded77e91cd9d80648f7dd40406cef73df841aa3c36f6907c8", size = 5391253, upload_time = "2025-04-23T01:46:37.608Z" }, + { url = "https://files.pythonhosted.org/packages/d6/10/5bf51858971c51ec96cfc13e800a9951f3fd501686f4c18d7d84fe2d6352/lxml-5.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4d885698f5019abe0de3d352caf9466d5de2baded00a06ef3f1216c1a58ae78f", size = 5261651, upload_time = "2025-04-23T01:46:40.183Z" }, + { url = "https://files.pythonhosted.org/packages/2b/11/06710dd809205377da380546f91d2ac94bad9ff735a72b64ec029f706c85/lxml-5.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea53d51859b6c64e7c51d522c03cc2c48b9b5d6172126854cc7f01aa11f52bc", size = 5024315, upload_time = "2025-04-23T01:46:43.333Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b0/15b6217834b5e3a59ebf7f53125e08e318030e8cc0d7310355e6edac98ef/lxml-5.4.0-cp312-cp312-win32.whl", hash = "sha256:d90b729fd2732df28130c064aac9bb8aff14ba20baa4aee7bd0795ff1187545f", size = 3486149, upload_time = "2025-04-23T01:46:45.684Z" }, + { url = "https://files.pythonhosted.org/packages/91/1e/05ddcb57ad2f3069101611bd5f5084157d90861a2ef460bf42f45cced944/lxml-5.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1dc4ca99e89c335a7ed47d38964abcb36c5910790f9bd106f2a8fa2ee0b909d2", size = 3817095, upload_time = "2025-04-23T01:46:48.521Z" }, ] [[package]] name = "markdown" version = "3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906, upload_time = "2025-04-11T14:42:50.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210 }, + { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210, upload_time = "2025-04-11T14:42:49.178Z" }, ] [[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 } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload_time = "2024-10-18T15:21:54.129Z" } wheels = [ - { 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 }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { 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, upload_time = "2024-10-18T15:21:02.187Z" }, + { 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, upload_time = "2024-10-18T15:21:02.941Z" }, + { 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, upload_time = "2024-10-18T15:21:03.953Z" }, + { 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, upload_time = "2024-10-18T15:21:06.495Z" }, + { 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, upload_time = "2024-10-18T15:21:07.295Z" }, + { 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, upload_time = "2024-10-18T15:21:08.073Z" }, + { 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, upload_time = "2024-10-18T15:21:09.318Z" }, + { 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, upload_time = "2024-10-18T15:21:10.185Z" }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload_time = "2024-10-18T15:21:11.005Z" }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload_time = "2024-10-18T15:21:12.911Z" }, + { 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, upload_time = "2024-10-18T15:21:13.777Z" }, + { 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, upload_time = "2024-10-18T15:21:14.822Z" }, + { 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, upload_time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload_time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload_time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload_time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload_time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload_time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload_time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload_time = "2024-10-18T15:21:23.499Z" }, ] [[package]] @@ -927,29 +934,29 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335, upload_time = "2025-02-27T19:19:51.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669 }, - { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996 }, - { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612 }, - { url = "https://files.pythonhosted.org/packages/40/b8/53fa08a5eaf78d3a7213fd6da1feec4bae14a81d9805e567013811ff0e85/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c", size = 8602258 }, - { url = "https://files.pythonhosted.org/packages/40/87/4397d2ce808467af86684a622dd112664553e81752ea8bf61bdd89d24a41/matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7", size = 9408896 }, - { url = "https://files.pythonhosted.org/packages/d7/68/0d03098b3feb786cbd494df0aac15b571effda7f7cbdec267e8a8d398c16/matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a", size = 8061281 }, - { url = "https://files.pythonhosted.org/packages/7c/1d/5e0dc3b59c034e43de16f94deb68f4ad8a96b3ea00f4b37c160b7474928e/matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107", size = 8175488 }, - { url = "https://files.pythonhosted.org/packages/7a/81/dae7e14042e74da658c3336ab9799128e09a1ee03964f2d89630b5d12106/matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be", size = 8046264 }, - { url = "https://files.pythonhosted.org/packages/21/c4/22516775dcde10fc9c9571d155f90710761b028fc44f660508106c363c97/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6", size = 8452048 }, - { url = "https://files.pythonhosted.org/packages/63/23/c0615001f67ce7c96b3051d856baedc0c818a2ed84570b9bf9bde200f85d/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d", size = 8597111 }, - { url = "https://files.pythonhosted.org/packages/ca/c0/a07939a82aed77770514348f4568177d7dadab9787ebc618a616fe3d665e/matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea", size = 9402771 }, - { url = "https://files.pythonhosted.org/packages/a6/b6/a9405484fb40746fdc6ae4502b16a9d6e53282ba5baaf9ebe2da579f68c4/matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c", size = 8063742 }, + { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669, upload_time = "2025-02-27T19:18:34.346Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996, upload_time = "2025-02-27T19:18:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612, upload_time = "2025-02-27T19:18:39.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/b8/53fa08a5eaf78d3a7213fd6da1feec4bae14a81d9805e567013811ff0e85/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c", size = 8602258, upload_time = "2025-02-27T19:18:43.217Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/4397d2ce808467af86684a622dd112664553e81752ea8bf61bdd89d24a41/matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7", size = 9408896, upload_time = "2025-02-27T19:18:45.852Z" }, + { url = "https://files.pythonhosted.org/packages/d7/68/0d03098b3feb786cbd494df0aac15b571effda7f7cbdec267e8a8d398c16/matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a", size = 8061281, upload_time = "2025-02-27T19:18:48.919Z" }, + { url = "https://files.pythonhosted.org/packages/7c/1d/5e0dc3b59c034e43de16f94deb68f4ad8a96b3ea00f4b37c160b7474928e/matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107", size = 8175488, upload_time = "2025-02-27T19:18:51.436Z" }, + { url = "https://files.pythonhosted.org/packages/7a/81/dae7e14042e74da658c3336ab9799128e09a1ee03964f2d89630b5d12106/matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be", size = 8046264, upload_time = "2025-02-27T19:18:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/21/c4/22516775dcde10fc9c9571d155f90710761b028fc44f660508106c363c97/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6", size = 8452048, upload_time = "2025-02-27T19:18:56.536Z" }, + { url = "https://files.pythonhosted.org/packages/63/23/c0615001f67ce7c96b3051d856baedc0c818a2ed84570b9bf9bde200f85d/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d", size = 8597111, upload_time = "2025-02-27T19:18:59.439Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c0/a07939a82aed77770514348f4568177d7dadab9787ebc618a616fe3d665e/matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea", size = 9402771, upload_time = "2025-02-27T19:19:01.944Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b6/a9405484fb40746fdc6ae4502b16a9d6e53282ba5baaf9ebe2da579f68c4/matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c", size = 8063742, upload_time = "2025-02-27T19:19:04.632Z" }, ] [[package]] name = "mergedeep" version = "1.3.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload_time = "2021-02-05T18:55:30.623Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload_time = "2021-02-05T18:55:29.583Z" }, ] [[package]] @@ -1025,9 +1032,9 @@ dependencies = [ { name = "pyyaml-env-tag" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload_time = "2024-08-30T12:24:06.899Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload_time = "2024-08-30T12:24:05.054Z" }, ] [[package]] @@ -1039,9 +1046,9 @@ dependencies = [ { name = "platformdirs" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload_time = "2023-11-20T17:51:09.981Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload_time = "2023-11-20T17:51:08.587Z" }, ] [[package]] @@ -1053,29 +1060,29 @@ dependencies = [ { name = "python3-xlib", marker = "sys_platform == 'linux'" }, { name = "rubicon-objc", marker = "sys_platform == 'darwin'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850 } +sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850, upload_time = "2020-03-27T21:20:10.136Z" } [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload_time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload_time = "2023-03-07T16:47:09.197Z" }, ] [[package]] name = "msal" -version = "1.32.0" +version = "1.32.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyjwt", extra = ["crypto"] }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/5f/ef42ef25fba682e83a8ee326a1a788e60c25affb58d014495349e37bce50/msal-1.32.0.tar.gz", hash = "sha256:5445fe3af1da6be484991a7ab32eaa82461dc2347de105b76af92c610c3335c2", size = 149817 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/90/81dcc50f0be11a8c4dcbae1a9f761a26e5f905231330a7cacc9f04ec4c61/msal-1.32.3.tar.gz", hash = "sha256:5eea038689c78a5a70ca8ecbe1245458b55a857bd096efb6989c69ba15985d35", size = 151449, upload_time = "2025-04-25T13:12:34.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/5a/2e663ef56a5d89eba962941b267ebe5be8c5ea340a9929d286e2f5fac505/msal-1.32.0-py3-none-any.whl", hash = "sha256:9dbac5384a10bbbf4dae5c7ea0d707d14e087b92c5aa4954b3feaa2d1aa0bcb7", size = 114655 }, + { url = "https://files.pythonhosted.org/packages/04/bf/81516b9aac7fd867709984d08eb4db1d2e3fe1df795c8e442cde9b568962/msal-1.32.3-py3-none-any.whl", hash = "sha256:b2798db57760b1961b142f027ffb7c8169536bf77316e99a0df5c4aaebb11569", size = 115358, upload_time = "2025-04-25T13:12:33.034Z" }, ] [[package]] @@ -1085,52 +1092,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "msal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315 } +sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload_time = "2025-03-14T23:51:03.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583 }, + { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload_time = "2025-03-14T23:51:03.016Z" }, ] [[package]] name = "multidict" version = "6.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372 } +sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372, upload_time = "2025-04-10T22:20:17.956Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259 }, - { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451 }, - { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706 }, - { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669 }, - { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182 }, - { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025 }, - { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481 }, - { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492 }, - { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279 }, - { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733 }, - { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089 }, - { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257 }, - { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728 }, - { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087 }, - { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137 }, - { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959 }, - { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541 }, - { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019 }, - { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925 }, - { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008 }, - { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374 }, - { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869 }, - { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949 }, - { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032 }, - { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517 }, - { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291 }, - { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982 }, - { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823 }, - { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714 }, - { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739 }, - { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809 }, - { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934 }, - { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242 }, - { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635 }, - { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400 }, + { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259, upload_time = "2025-04-10T22:17:59.632Z" }, + { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451, upload_time = "2025-04-10T22:18:01.202Z" }, + { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706, upload_time = "2025-04-10T22:18:02.276Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669, upload_time = "2025-04-10T22:18:03.436Z" }, + { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182, upload_time = "2025-04-10T22:18:04.922Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025, upload_time = "2025-04-10T22:18:06.274Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481, upload_time = "2025-04-10T22:18:07.742Z" }, + { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492, upload_time = "2025-04-10T22:18:09.095Z" }, + { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279, upload_time = "2025-04-10T22:18:10.474Z" }, + { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733, upload_time = "2025-04-10T22:18:11.793Z" }, + { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089, upload_time = "2025-04-10T22:18:13.153Z" }, + { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257, upload_time = "2025-04-10T22:18:14.654Z" }, + { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728, upload_time = "2025-04-10T22:18:16.236Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087, upload_time = "2025-04-10T22:18:17.979Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137, upload_time = "2025-04-10T22:18:19.362Z" }, + { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959, upload_time = "2025-04-10T22:18:20.728Z" }, + { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541, upload_time = "2025-04-10T22:18:22.001Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019, upload_time = "2025-04-10T22:18:23.174Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925, upload_time = "2025-04-10T22:18:24.834Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008, upload_time = "2025-04-10T22:18:26.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374, upload_time = "2025-04-10T22:18:27.714Z" }, + { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869, upload_time = "2025-04-10T22:18:29.162Z" }, + { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949, upload_time = "2025-04-10T22:18:30.679Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032, upload_time = "2025-04-10T22:18:32.146Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517, upload_time = "2025-04-10T22:18:33.538Z" }, + { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291, upload_time = "2025-04-10T22:18:34.962Z" }, + { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982, upload_time = "2025-04-10T22:18:36.443Z" }, + { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823, upload_time = "2025-04-10T22:18:37.924Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714, upload_time = "2025-04-10T22:18:39.807Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739, upload_time = "2025-04-10T22:18:41.341Z" }, + { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809, upload_time = "2025-04-10T22:18:42.817Z" }, + { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934, upload_time = "2025-04-10T22:18:44.311Z" }, + { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242, upload_time = "2025-04-10T22:18:46.193Z" }, + { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635, upload_time = "2025-04-10T22:18:47.498Z" }, + { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400, upload_time = "2025-04-10T22:20:16.445Z" }, ] [[package]] @@ -1141,67 +1148,67 @@ dependencies = [ { name = "mypy-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload_time = "2025-02-05T03:50:34.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, - { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, - { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, - { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 }, - { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 }, - { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 }, - { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, - { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, - { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, - { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, - { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, - { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, - { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, + { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload_time = "2025-02-05T03:50:17.287Z" }, + { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload_time = "2025-02-05T03:49:51.21Z" }, + { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload_time = "2025-02-05T03:50:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload_time = "2025-02-05T03:49:42.408Z" }, + { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload_time = "2025-02-05T03:49:07.707Z" }, + { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload_time = "2025-02-05T03:49:54.581Z" }, + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload_time = "2025-02-05T03:50:28.25Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload_time = "2025-02-05T03:50:13.411Z" }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload_time = "2025-02-05T03:50:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload_time = "2025-02-05T03:48:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload_time = "2025-02-05T03:49:03.628Z" }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload_time = "2025-02-05T03:50:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload_time = "2025-02-05T03:50:08.348Z" }, ] [[package]] name = "mypy-extensions" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload_time = "2025-04-22T14:54:24.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload_time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "natsort" version = "8.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575 } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload_time = "2023-06-20T04:17:19.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268 }, + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload_time = "2023-06-20T04:17:17.522Z" }, ] [[package]] name = "numpy" version = "2.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 } +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090, upload_time = "2024-11-02T17:48:55.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252 }, - { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119 }, - { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978 }, - { url = "https://files.pythonhosted.org/packages/09/ac/61d07930a4993dd9691a6432de16d93bbe6aa4b1c12a5e573d468eefc1ca/numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09", size = 6892570 }, - { url = "https://files.pythonhosted.org/packages/27/2f/21b94664f23af2bb52030653697c685022119e0dc93d6097c3cb45bce5f9/numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a", size = 13896715 }, - { url = "https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b", size = 16339644 }, - { url = "https://files.pythonhosted.org/packages/fa/81/ce213159a1ed8eb7d88a2a6ef4fbdb9e4ffd0c76b866c350eb4e3c37e640/numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee", size = 16712217 }, - { url = "https://files.pythonhosted.org/packages/7d/84/4de0b87d5a72f45556b2a8ee9fc8801e8518ec867fc68260c1f5dcb3903f/numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0", size = 14399053 }, - { url = "https://files.pythonhosted.org/packages/7e/1c/e5fabb9ad849f9d798b44458fd12a318d27592d4bc1448e269dec070ff04/numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9", size = 6534741 }, - { url = "https://files.pythonhosted.org/packages/1e/48/a9a4b538e28f854bfb62e1dea3c8fea12e90216a276c7777ae5345ff29a7/numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2", size = 12869487 }, - { url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658 }, - { url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258 }, - { url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249 }, - { url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704 }, - { url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089 }, - { url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185 }, - { url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751 }, - { url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705 }, - { url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077 }, - { url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858 }, + { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252, upload_time = "2024-11-02T17:34:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119, upload_time = "2024-11-02T17:34:23.809Z" }, + { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978, upload_time = "2024-11-02T17:34:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/09/ac/61d07930a4993dd9691a6432de16d93bbe6aa4b1c12a5e573d468eefc1ca/numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09", size = 6892570, upload_time = "2024-11-02T17:34:45.401Z" }, + { url = "https://files.pythonhosted.org/packages/27/2f/21b94664f23af2bb52030653697c685022119e0dc93d6097c3cb45bce5f9/numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a", size = 13896715, upload_time = "2024-11-02T17:35:06.564Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b", size = 16339644, upload_time = "2024-11-02T17:35:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/fa/81/ce213159a1ed8eb7d88a2a6ef4fbdb9e4ffd0c76b866c350eb4e3c37e640/numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee", size = 16712217, upload_time = "2024-11-02T17:35:56.703Z" }, + { url = "https://files.pythonhosted.org/packages/7d/84/4de0b87d5a72f45556b2a8ee9fc8801e8518ec867fc68260c1f5dcb3903f/numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0", size = 14399053, upload_time = "2024-11-02T17:36:22.3Z" }, + { url = "https://files.pythonhosted.org/packages/7e/1c/e5fabb9ad849f9d798b44458fd12a318d27592d4bc1448e269dec070ff04/numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9", size = 6534741, upload_time = "2024-11-02T17:36:33.552Z" }, + { url = "https://files.pythonhosted.org/packages/1e/48/a9a4b538e28f854bfb62e1dea3c8fea12e90216a276c7777ae5345ff29a7/numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2", size = 12869487, upload_time = "2024-11-02T17:36:52.909Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658, upload_time = "2024-11-02T17:37:23.919Z" }, + { url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258, upload_time = "2024-11-02T17:37:45.252Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249, upload_time = "2024-11-02T17:37:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704, upload_time = "2024-11-02T17:38:05.127Z" }, + { url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089, upload_time = "2024-11-02T17:38:25.997Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185, upload_time = "2024-11-02T17:38:51.07Z" }, + { url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751, upload_time = "2024-11-02T17:39:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705, upload_time = "2024-11-02T17:39:38.274Z" }, + { url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077, upload_time = "2024-11-02T17:39:49.299Z" }, + { url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858, upload_time = "2024-11-02T17:40:08.851Z" }, ] [[package]] @@ -1212,18 +1219,18 @@ dependencies = [ { name = "numpy" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/54/0e385c26bf230d223810a9c7d06628d954008a5e5e4b73ee26ef02327282/onnx-1.17.0.tar.gz", hash = "sha256:48ca1a91ff73c1d5e3ea2eef20ae5d0e709bb8a2355ed798ffc2169753013fd3", size = 12165120 } +sdist = { url = "https://files.pythonhosted.org/packages/9a/54/0e385c26bf230d223810a9c7d06628d954008a5e5e4b73ee26ef02327282/onnx-1.17.0.tar.gz", hash = "sha256:48ca1a91ff73c1d5e3ea2eef20ae5d0e709bb8a2355ed798ffc2169753013fd3", size = 12165120, upload_time = "2024-10-01T21:48:40.63Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a9/8d1b1d53aec70df53e0f57e9f9fcf47004276539e29230c3d5f1f50719ba/onnx-1.17.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:d6fc3a03fc0129b8b6ac03f03bc894431ffd77c7d79ec023d0afd667b4d35869", size = 16647991 }, - { url = "https://files.pythonhosted.org/packages/7b/e3/cc80110e5996ca61878f7b4c73c7a286cd88918ff35eacb60dc75ab11ef5/onnx-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a4b63d4e1d8ec3e2f069e7b798b2955810aa434f7361f01bc8ca08d69cce4", size = 15908949 }, - { url = "https://files.pythonhosted.org/packages/b1/2f/91092557ed478e323a2b4471e2081fdf88d1dd52ae988ceaf7db4e4506ff/onnx-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a183c6178be001bf398260e5ac2c927dc43e7746e8638d6c05c20e321f8c949", size = 16048190 }, - { url = "https://files.pythonhosted.org/packages/ac/59/9ea23fc22d0bb853133f363e6248e31bcbc6c1c90543a3938c00412ac02a/onnx-1.17.0-cp311-cp311-win32.whl", hash = "sha256:081ec43a8b950171767d99075b6b92553901fa429d4bc5eb3ad66b36ef5dbe3a", size = 14424299 }, - { url = "https://files.pythonhosted.org/packages/51/a5/19b0dfcb567b62e7adf1a21b08b23224f0c2d13842aee4d0abc6f07f9cf5/onnx-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:95c03e38671785036bb704c30cd2e150825f6ab4763df3a4f1d249da48525957", size = 14529142 }, - { url = "https://files.pythonhosted.org/packages/b4/dd/c416a11a28847fafb0db1bf43381979a0f522eb9107b831058fde012dd56/onnx-1.17.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:0e906e6a83437de05f8139ea7eaf366bf287f44ae5cc44b2850a30e296421f2f", size = 16651271 }, - { url = "https://files.pythonhosted.org/packages/f0/6c/f040652277f514ecd81b7251841f96caa5538365af7df07f86c6018cda2b/onnx-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d955ba2939878a520a97614bcf2e79c1df71b29203e8ced478fa78c9a9c63c2", size = 15907522 }, - { url = "https://files.pythonhosted.org/packages/3d/7c/67f4952d1b56b3f74a154b97d0dd0630d525923b354db117d04823b8b49b/onnx-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3fb5cc4e2898ac5312a7dc03a65133dd2abf9a5e520e69afb880a7251ec97a", size = 16046307 }, - { url = "https://files.pythonhosted.org/packages/ae/20/6da11042d2ab870dfb4ce4a6b52354d7651b6b4112038b6d2229ab9904c4/onnx-1.17.0-cp312-cp312-win32.whl", hash = "sha256:317870fca3349d19325a4b7d1b5628f6de3811e9710b1e3665c68b073d0e68d7", size = 14424235 }, - { url = "https://files.pythonhosted.org/packages/35/55/c4d11bee1fdb0c4bd84b4e3562ff811a19b63266816870ae1f95567aa6e1/onnx-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:659b8232d627a5460d74fd3c96947ae83db6d03f035ac633e20cd69cfa029227", size = 14530453 }, + { url = "https://files.pythonhosted.org/packages/e5/a9/8d1b1d53aec70df53e0f57e9f9fcf47004276539e29230c3d5f1f50719ba/onnx-1.17.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:d6fc3a03fc0129b8b6ac03f03bc894431ffd77c7d79ec023d0afd667b4d35869", size = 16647991, upload_time = "2024-10-01T21:46:02.491Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e3/cc80110e5996ca61878f7b4c73c7a286cd88918ff35eacb60dc75ab11ef5/onnx-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a4b63d4e1d8ec3e2f069e7b798b2955810aa434f7361f01bc8ca08d69cce4", size = 15908949, upload_time = "2024-10-01T21:46:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2f/91092557ed478e323a2b4471e2081fdf88d1dd52ae988ceaf7db4e4506ff/onnx-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a183c6178be001bf398260e5ac2c927dc43e7746e8638d6c05c20e321f8c949", size = 16048190, upload_time = "2024-10-01T21:46:08.041Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/9ea23fc22d0bb853133f363e6248e31bcbc6c1c90543a3938c00412ac02a/onnx-1.17.0-cp311-cp311-win32.whl", hash = "sha256:081ec43a8b950171767d99075b6b92553901fa429d4bc5eb3ad66b36ef5dbe3a", size = 14424299, upload_time = "2024-10-01T21:46:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/51/a5/19b0dfcb567b62e7adf1a21b08b23224f0c2d13842aee4d0abc6f07f9cf5/onnx-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:95c03e38671785036bb704c30cd2e150825f6ab4763df3a4f1d249da48525957", size = 14529142, upload_time = "2024-10-01T21:46:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dd/c416a11a28847fafb0db1bf43381979a0f522eb9107b831058fde012dd56/onnx-1.17.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:0e906e6a83437de05f8139ea7eaf366bf287f44ae5cc44b2850a30e296421f2f", size = 16651271, upload_time = "2024-10-01T21:46:16.084Z" }, + { url = "https://files.pythonhosted.org/packages/f0/6c/f040652277f514ecd81b7251841f96caa5538365af7df07f86c6018cda2b/onnx-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d955ba2939878a520a97614bcf2e79c1df71b29203e8ced478fa78c9a9c63c2", size = 15907522, upload_time = "2024-10-01T21:46:18.574Z" }, + { url = "https://files.pythonhosted.org/packages/3d/7c/67f4952d1b56b3f74a154b97d0dd0630d525923b354db117d04823b8b49b/onnx-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3fb5cc4e2898ac5312a7dc03a65133dd2abf9a5e520e69afb880a7251ec97a", size = 16046307, upload_time = "2024-10-01T21:46:21.186Z" }, + { url = "https://files.pythonhosted.org/packages/ae/20/6da11042d2ab870dfb4ce4a6b52354d7651b6b4112038b6d2229ab9904c4/onnx-1.17.0-cp312-cp312-win32.whl", hash = "sha256:317870fca3349d19325a4b7d1b5628f6de3811e9710b1e3665c68b073d0e68d7", size = 14424235, upload_time = "2024-10-01T21:46:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/35/55/c4d11bee1fdb0c4bd84b4e3562ff811a19b63266816870ae1f95567aa6e1/onnx-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:659b8232d627a5460d74fd3c96947ae83db6d03f035ac633e20cd69cfa029227", size = 14530453, upload_time = "2024-10-01T21:46:26.981Z" }, ] [[package]] @@ -1233,14 +1240,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929 } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload_time = "2025-01-16T13:53:40.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460 }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330 }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060 }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856 }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425 }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386 }, + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload_time = "2025-01-16T13:52:57.015Z" }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload_time = "2025-01-16T13:55:45.731Z" }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload_time = "2025-01-16T13:51:59.625Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload_time = "2025-01-16T13:53:29.654Z" }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload_time = "2025-01-16T13:52:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload_time = "2025-01-16T13:52:56.418Z" }, ] [[package]] @@ -1411,11 +1418,11 @@ provides-extras = ["docs", "testing", "dev", "tools"] [[package]] name = "packaging" -version = "24.2" +version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload_time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload_time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -1423,17 +1430,17 @@ name = "panda3d" version = "1.10.14" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/9a/31d07e3d7c1b40335e8418c540d63f4d33c571648ed8d69896ab778e65c3/panda3d-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54b8ef9fe3684960a2c7d47b0d63c0354c17bc516795e59db6c1e5bda8c16c1c", size = 67700752 }, - { url = "https://files.pythonhosted.org/packages/61/05/fce327535d8907ac01f43813c980f30ea86d37db62c340847519ea2ab222/panda3d-1.10.14-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:93414675894b18eea8d27edc1bbd1dc719eae207d45ec263d47195504bc4705f", size = 118966179 }, - { url = "https://files.pythonhosted.org/packages/8a/54/24e205231e7b1bced58ba9620fbec7114673d821fc7ad9ed1804cab556b4/panda3d-1.10.14-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:d1bc0d926f90c8fa14a1587fa9dbe5f89a4eda8c9684fa183a8eaa35fc8e891a", size = 55145295 }, - { url = "https://files.pythonhosted.org/packages/06/d3/38e989822292935d7473d35117099f71481cc6b104cb2dd048cb8058a5a8/panda3d-1.10.14-cp311-cp311-win32.whl", hash = "sha256:1039340a4a7965fe4c3e904edb4fff691584df435a154fecccf534587cd07a34", size = 53137177 }, - { url = "https://files.pythonhosted.org/packages/5c/32/b16c81661ed0d8ad62976004d81845baa321e53314e253ef0841155be770/panda3d-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:1ddf01040b9c5497fb8659e3c5ef793a26c869cfdfb1b75e6d04d6fba0c03b73", size = 64447666 }, - { url = "https://files.pythonhosted.org/packages/5a/d4/90e98993b1a3f3c9fae83267f8c51186e676a8c1365c4180dfc65cd7ba62/panda3d-1.10.14-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1bfbcee77779f12ecce6a3d5a856e573b25d6343f8c4b107e814d9702e70a65d", size = 67839196 }, - { url = "https://files.pythonhosted.org/packages/dc/e5/862821575073863ce49cc57b8349b47cb25ce11feae0e419b3d023ac1a69/panda3d-1.10.14-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:bc6540c5559d7e14a8992eff7de0157b7c42406b7ba221941ed224289496841c", size = 119271341 }, - { url = "https://files.pythonhosted.org/packages/f4/20/f16d91805777825e530037177d9075c83da7384f12b778b133e3164a31f3/panda3d-1.10.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:143daab1ce6bedcba711ea3f6cab0ebe5082f22c5f43e7178fadd2dd01209da7", size = 47604077 }, - { url = "https://files.pythonhosted.org/packages/11/69/806dcdbaee3e8deee1956abeea0d3d3e504315d2e9814de82a44809a8617/panda3d-1.10.14-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:3c4399a286a142de7ff86f9356d7e526bbbd38892d7f7d39fecb5c33064972bc", size = 55539594 }, - { url = "https://files.pythonhosted.org/packages/ad/25/005de5e2b6d0acd546f8b3f2b547cd29e308cdd04a397f0ea68046e26571/panda3d-1.10.14-cp312-cp312-win32.whl", hash = "sha256:e92e0dd907e2af33085a2c31ca2263dc8023b1b7bc70ce1b9fbc84631e130e51", size = 53479813 }, - { url = "https://files.pythonhosted.org/packages/74/bb/cb57563855da994614a33f57bd5691fbcd69f12e5ccddd30d387d0be287f/panda3d-1.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:a5f2defd822d38848f8ae1956115adcb6cc7f464f03a67e73681cc72df125ef4", size = 64893222 }, + { url = "https://files.pythonhosted.org/packages/f5/9a/31d07e3d7c1b40335e8418c540d63f4d33c571648ed8d69896ab778e65c3/panda3d-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54b8ef9fe3684960a2c7d47b0d63c0354c17bc516795e59db6c1e5bda8c16c1c", size = 67700752, upload_time = "2024-01-08T19:05:55.559Z" }, + { url = "https://files.pythonhosted.org/packages/61/05/fce327535d8907ac01f43813c980f30ea86d37db62c340847519ea2ab222/panda3d-1.10.14-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:93414675894b18eea8d27edc1bbd1dc719eae207d45ec263d47195504bc4705f", size = 118966179, upload_time = "2024-01-08T19:06:03.165Z" }, + { url = "https://files.pythonhosted.org/packages/8a/54/24e205231e7b1bced58ba9620fbec7114673d821fc7ad9ed1804cab556b4/panda3d-1.10.14-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:d1bc0d926f90c8fa14a1587fa9dbe5f89a4eda8c9684fa183a8eaa35fc8e891a", size = 55145295, upload_time = "2024-01-08T19:06:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/06/d3/38e989822292935d7473d35117099f71481cc6b104cb2dd048cb8058a5a8/panda3d-1.10.14-cp311-cp311-win32.whl", hash = "sha256:1039340a4a7965fe4c3e904edb4fff691584df435a154fecccf534587cd07a34", size = 53137177, upload_time = "2024-01-08T19:06:15.901Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/b16c81661ed0d8ad62976004d81845baa321e53314e253ef0841155be770/panda3d-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:1ddf01040b9c5497fb8659e3c5ef793a26c869cfdfb1b75e6d04d6fba0c03b73", size = 64447666, upload_time = "2024-01-08T19:06:22.105Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d4/90e98993b1a3f3c9fae83267f8c51186e676a8c1365c4180dfc65cd7ba62/panda3d-1.10.14-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1bfbcee77779f12ecce6a3d5a856e573b25d6343f8c4b107e814d9702e70a65d", size = 67839196, upload_time = "2024-01-08T19:01:00.417Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e5/862821575073863ce49cc57b8349b47cb25ce11feae0e419b3d023ac1a69/panda3d-1.10.14-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:bc6540c5559d7e14a8992eff7de0157b7c42406b7ba221941ed224289496841c", size = 119271341, upload_time = "2024-01-08T19:01:09.455Z" }, + { url = "https://files.pythonhosted.org/packages/f4/20/f16d91805777825e530037177d9075c83da7384f12b778b133e3164a31f3/panda3d-1.10.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:143daab1ce6bedcba711ea3f6cab0ebe5082f22c5f43e7178fadd2dd01209da7", size = 47604077, upload_time = "2024-05-28T20:25:37.118Z" }, + { url = "https://files.pythonhosted.org/packages/11/69/806dcdbaee3e8deee1956abeea0d3d3e504315d2e9814de82a44809a8617/panda3d-1.10.14-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:3c4399a286a142de7ff86f9356d7e526bbbd38892d7f7d39fecb5c33064972bc", size = 55539594, upload_time = "2024-01-08T19:01:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/ad/25/005de5e2b6d0acd546f8b3f2b547cd29e308cdd04a397f0ea68046e26571/panda3d-1.10.14-cp312-cp312-win32.whl", hash = "sha256:e92e0dd907e2af33085a2c31ca2263dc8023b1b7bc70ce1b9fbc84631e130e51", size = 53479813, upload_time = "2024-01-08T19:01:20.923Z" }, + { url = "https://files.pythonhosted.org/packages/74/bb/cb57563855da994614a33f57bd5691fbcd69f12e5ccddd30d387d0be287f/panda3d-1.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:a5f2defd822d38848f8ae1956115adcb6cc7f464f03a67e73681cc72df125ef4", size = 64893222, upload_time = "2024-01-08T19:01:26.347Z" }, ] [[package]] @@ -1444,9 +1451,9 @@ dependencies = [ { name = "panda3d" }, { name = "panda3d-simplepbr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573 } +sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload_time = "2021-05-21T05:46:32.738Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/94/98ed1f81ca0f5daf6de80533805cc1e98ac162abe4e3e1d382caa7ba5c3c/panda3d_gltf-0.13-py3-none-any.whl", hash = "sha256:02d1a980f447bb1895ff4b48c667f289ba78f07a28ef308d8839b665a621efe2", size = 25568 }, + { url = "https://files.pythonhosted.org/packages/70/94/98ed1f81ca0f5daf6de80533805cc1e98ac162abe4e3e1d382caa7ba5c3c/panda3d_gltf-0.13-py3-none-any.whl", hash = "sha256:02d1a980f447bb1895ff4b48c667f289ba78f07a28ef308d8839b665a621efe2", size = 25568, upload_time = "2021-05-21T05:46:31.28Z" }, ] [[package]] @@ -1457,82 +1464,82 @@ dependencies = [ { name = "panda3d" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055 } +sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload_time = "2025-03-30T16:57:41.087Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/5d/3744c6550dddf933785a37cdd4a9921fe13284e6d115b5a2637fe390f158/panda3d_simplepbr-0.13.1-py3-none-any.whl", hash = "sha256:cda41cb57cff035b851646956cfbdcc408bee42511dabd4f2d7bd4fbf48c57a9", size = 2457097 }, + { url = "https://files.pythonhosted.org/packages/11/5d/3744c6550dddf933785a37cdd4a9921fe13284e6d115b5a2637fe390f158/panda3d_simplepbr-0.13.1-py3-none-any.whl", hash = "sha256:cda41cb57cff035b851646956cfbdcc408bee42511dabd4f2d7bd4fbf48c57a9", size = 2457097, upload_time = "2025-03-30T16:57:39.729Z" }, ] [[package]] name = "parameterized" version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c6/23/2288f308d238b4f261c039cafcd650435d624de97c6ffc903f06ea8af50f/parameterized-0.8.1.tar.gz", hash = "sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c", size = 23936 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/23/2288f308d238b4f261c039cafcd650435d624de97c6ffc903f06ea8af50f/parameterized-0.8.1.tar.gz", hash = "sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c", size = 23936, upload_time = "2021-01-09T20:35:18.235Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/13/fe468c8c7400a8eca204e6e160a29bf7dcd45a76e20f1c030f3eaa690d93/parameterized-0.8.1-py2.py3-none-any.whl", hash = "sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9", size = 26354 }, + { url = "https://files.pythonhosted.org/packages/31/13/fe468c8c7400a8eca204e6e160a29bf7dcd45a76e20f1c030f3eaa690d93/parameterized-0.8.1-py2.py3-none-any.whl", hash = "sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9", size = 26354, upload_time = "2021-01-09T20:35:16.307Z" }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload_time = "2023-12-10T22:30:45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload_time = "2023-12-10T22:30:43.14Z" }, ] [[package]] name = "pillow" version = "11.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707 } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload_time = "2025-04-12T17:50:03.289Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450 }, - { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550 }, - { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018 }, - { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006 }, - { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773 }, - { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069 }, - { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460 }, - { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304 }, - { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809 }, - { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338 }, - { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918 }, - { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185 }, - { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306 }, - { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121 }, - { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707 }, - { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921 }, - { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523 }, - { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836 }, - { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390 }, - { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309 }, - { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768 }, - { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087 }, - { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734 }, - { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841 }, - { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470 }, - { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013 }, - { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165 }, - { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586 }, - { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 }, + { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450, upload_time = "2025-04-12T17:47:37.135Z" }, + { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550, upload_time = "2025-04-12T17:47:39.345Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018, upload_time = "2025-04-12T17:47:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006, upload_time = "2025-04-12T17:47:42.912Z" }, + { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773, upload_time = "2025-04-12T17:47:44.611Z" }, + { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069, upload_time = "2025-04-12T17:47:46.46Z" }, + { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460, upload_time = "2025-04-12T17:47:49.255Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304, upload_time = "2025-04-12T17:47:51.067Z" }, + { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809, upload_time = "2025-04-12T17:47:54.425Z" }, + { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338, upload_time = "2025-04-12T17:47:56.535Z" }, + { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918, upload_time = "2025-04-12T17:47:58.217Z" }, + { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185, upload_time = "2025-04-12T17:48:00.417Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306, upload_time = "2025-04-12T17:48:02.391Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121, upload_time = "2025-04-12T17:48:04.554Z" }, + { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707, upload_time = "2025-04-12T17:48:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921, upload_time = "2025-04-12T17:48:09.229Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523, upload_time = "2025-04-12T17:48:11.631Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836, upload_time = "2025-04-12T17:48:13.592Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390, upload_time = "2025-04-12T17:48:15.938Z" }, + { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309, upload_time = "2025-04-12T17:48:17.885Z" }, + { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768, upload_time = "2025-04-12T17:48:19.655Z" }, + { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087, upload_time = "2025-04-12T17:48:21.991Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734, upload_time = "2025-04-12T17:49:46.789Z" }, + { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841, upload_time = "2025-04-12T17:49:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470, upload_time = "2025-04-12T17:49:50.831Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013, upload_time = "2025-04-12T17:49:53.278Z" }, + { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165, upload_time = "2025-04-12T17:49:55.164Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586, upload_time = "2025-04-12T17:49:57.171Z" }, + { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751, upload_time = "2025-04-12T17:49:59.628Z" }, ] [[package]] name = "platformdirs" version = "4.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291 } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload_time = "2025-03-19T20:36:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499 }, + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload_time = "2025-03-19T20:36:09.038Z" }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload_time = "2024-04-20T21:34:42.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload_time = "2024-04-20T21:34:40.434Z" }, ] [[package]] @@ -1542,119 +1549,119 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/7d/3299241a753c738d114600c360d754550b28c285281dc6a5132c4ccfae65/pre_commit_hooks-5.0.0.tar.gz", hash = "sha256:10626959a9eaf602fbfc22bc61b6e75801436f82326bfcee82bb1f2fc4bc646e", size = 29747 } +sdist = { url = "https://files.pythonhosted.org/packages/ac/7d/3299241a753c738d114600c360d754550b28c285281dc6a5132c4ccfae65/pre_commit_hooks-5.0.0.tar.gz", hash = "sha256:10626959a9eaf602fbfc22bc61b6e75801436f82326bfcee82bb1f2fc4bc646e", size = 29747, upload_time = "2024-10-05T18:43:11.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/29/db1d855a661c02dbde5cab3057969133fcc62e7a0c6393e48fe9d0e81679/pre_commit_hooks-5.0.0-py2.py3-none-any.whl", hash = "sha256:8d71cfb582c5c314a5498d94e0104b6567a8b93fb35903ea845c491f4e290a7a", size = 41245 }, + { url = "https://files.pythonhosted.org/packages/1e/29/db1d855a661c02dbde5cab3057969133fcc62e7a0c6393e48fe9d0e81679/pre_commit_hooks-5.0.0-py2.py3-none-any.whl", hash = "sha256:8d71cfb582c5c314a5498d94e0104b6567a8b93fb35903ea845c491f4e290a7a", size = 41245, upload_time = "2024-10-05T18:43:09.901Z" }, ] [[package]] name = "progressbar" version = "2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/a6/b8e451f6cff1c99b4747a2f7235aa904d2d49e8e1464e0b798272aa84358/progressbar-2.5.tar.gz", hash = "sha256:5d81cb529da2e223b53962afd6c8ca0f05c6670e40309a7219eacc36af9b6c63", size = 10046 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/a6/b8e451f6cff1c99b4747a2f7235aa904d2d49e8e1464e0b798272aa84358/progressbar-2.5.tar.gz", hash = "sha256:5d81cb529da2e223b53962afd6c8ca0f05c6670e40309a7219eacc36af9b6c63", size = 10046, upload_time = "2018-06-29T02:32:00.222Z" } [[package]] name = "propcache" version = "0.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651, upload_time = "2025-03-26T03:06:12.05Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243 }, - { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503 }, - { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934 }, - { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633 }, - { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124 }, - { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283 }, - { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498 }, - { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486 }, - { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675 }, - { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727 }, - { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878 }, - { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558 }, - { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754 }, - { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088 }, - { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859 }, - { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153 }, - { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430 }, - { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637 }, - { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123 }, - { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031 }, - { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100 }, - { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170 }, - { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000 }, - { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262 }, - { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772 }, - { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133 }, - { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741 }, - { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047 }, - { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467 }, - { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022 }, - { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647 }, - { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784 }, - { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, + { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243, upload_time = "2025-03-26T03:04:01.912Z" }, + { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503, upload_time = "2025-03-26T03:04:03.704Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934, upload_time = "2025-03-26T03:04:05.257Z" }, + { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633, upload_time = "2025-03-26T03:04:07.044Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124, upload_time = "2025-03-26T03:04:08.676Z" }, + { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283, upload_time = "2025-03-26T03:04:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498, upload_time = "2025-03-26T03:04:11.616Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486, upload_time = "2025-03-26T03:04:13.102Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675, upload_time = "2025-03-26T03:04:14.658Z" }, + { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727, upload_time = "2025-03-26T03:04:16.207Z" }, + { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878, upload_time = "2025-03-26T03:04:18.11Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558, upload_time = "2025-03-26T03:04:19.562Z" }, + { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754, upload_time = "2025-03-26T03:04:21.065Z" }, + { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088, upload_time = "2025-03-26T03:04:22.718Z" }, + { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859, upload_time = "2025-03-26T03:04:24.039Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153, upload_time = "2025-03-26T03:04:25.211Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430, upload_time = "2025-03-26T03:04:26.436Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637, upload_time = "2025-03-26T03:04:27.932Z" }, + { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123, upload_time = "2025-03-26T03:04:30.659Z" }, + { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031, upload_time = "2025-03-26T03:04:31.977Z" }, + { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100, upload_time = "2025-03-26T03:04:33.45Z" }, + { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170, upload_time = "2025-03-26T03:04:35.542Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000, upload_time = "2025-03-26T03:04:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262, upload_time = "2025-03-26T03:04:39.532Z" }, + { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772, upload_time = "2025-03-26T03:04:41.109Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133, upload_time = "2025-03-26T03:04:42.544Z" }, + { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741, upload_time = "2025-03-26T03:04:44.06Z" }, + { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047, upload_time = "2025-03-26T03:04:45.983Z" }, + { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467, upload_time = "2025-03-26T03:04:47.699Z" }, + { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022, upload_time = "2025-03-26T03:04:49.195Z" }, + { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647, upload_time = "2025-03-26T03:04:50.595Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784, upload_time = "2025-03-26T03:04:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376, upload_time = "2025-03-26T03:06:10.5Z" }, ] [[package]] name = "protobuf" version = "6.30.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315 } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315, upload_time = "2025-03-26T19:12:57.394Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148 }, - { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003 }, - { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579 }, - { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319 }, - { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212 }, - { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062 }, + { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148, upload_time = "2025-03-26T19:12:41.359Z" }, + { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003, upload_time = "2025-03-26T19:12:44.156Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579, upload_time = "2025-03-26T19:12:45.447Z" }, + { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319, upload_time = "2025-03-26T19:12:46.999Z" }, + { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212, upload_time = "2025-03-26T19:12:48.458Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062, upload_time = "2025-03-26T19:12:55.892Z" }, ] [[package]] name = "psutil" version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload_time = "2025-02-13T21:54:07.946Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload_time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload_time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload_time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload_time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload_time = "2025-02-13T21:54:24.68Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload_time = "2025-02-13T21:54:34.31Z" }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload_time = "2025-02-13T21:54:37.486Z" }, ] [[package]] name = "pyarrow" version = "19.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437, upload_time = "2025-02-18T18:55:57.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/55/f1a8d838ec07fe3ca53edbe76f782df7b9aafd4417080eebf0b42aab0c52/pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90", size = 30713987 }, - { url = "https://files.pythonhosted.org/packages/13/12/428861540bb54c98a140ae858a11f71d041ef9e501e6b7eb965ca7909505/pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00", size = 32135613 }, - { url = "https://files.pythonhosted.org/packages/2f/8a/23d7cc5ae2066c6c736bce1db8ea7bc9ac3ef97ac7e1c1667706c764d2d9/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae", size = 41149147 }, - { url = "https://files.pythonhosted.org/packages/a2/7a/845d151bb81a892dfb368bf11db584cf8b216963ccce40a5cf50a2492a18/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5", size = 42178045 }, - { url = "https://files.pythonhosted.org/packages/a7/31/e7282d79a70816132cf6cae7e378adfccce9ae10352d21c2fecf9d9756dd/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3", size = 40532998 }, - { url = "https://files.pythonhosted.org/packages/b8/82/20f3c290d6e705e2ee9c1fa1d5a0869365ee477e1788073d8b548da8b64c/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6", size = 42084055 }, - { url = "https://files.pythonhosted.org/packages/ff/77/e62aebd343238863f2c9f080ad2ef6ace25c919c6ab383436b5b81cbeef7/pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466", size = 25283133 }, - { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749 }, - { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007 }, - { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566 }, - { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991 }, - { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986 }, - { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026 }, - { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108 }, + { url = "https://files.pythonhosted.org/packages/a0/55/f1a8d838ec07fe3ca53edbe76f782df7b9aafd4417080eebf0b42aab0c52/pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90", size = 30713987, upload_time = "2025-02-18T18:52:20.463Z" }, + { url = "https://files.pythonhosted.org/packages/13/12/428861540bb54c98a140ae858a11f71d041ef9e501e6b7eb965ca7909505/pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00", size = 32135613, upload_time = "2025-02-18T18:52:25.29Z" }, + { url = "https://files.pythonhosted.org/packages/2f/8a/23d7cc5ae2066c6c736bce1db8ea7bc9ac3ef97ac7e1c1667706c764d2d9/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae", size = 41149147, upload_time = "2025-02-18T18:52:30.975Z" }, + { url = "https://files.pythonhosted.org/packages/a2/7a/845d151bb81a892dfb368bf11db584cf8b216963ccce40a5cf50a2492a18/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5", size = 42178045, upload_time = "2025-02-18T18:52:36.859Z" }, + { url = "https://files.pythonhosted.org/packages/a7/31/e7282d79a70816132cf6cae7e378adfccce9ae10352d21c2fecf9d9756dd/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3", size = 40532998, upload_time = "2025-02-18T18:52:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/b8/82/20f3c290d6e705e2ee9c1fa1d5a0869365ee477e1788073d8b548da8b64c/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6", size = 42084055, upload_time = "2025-02-18T18:52:48.749Z" }, + { url = "https://files.pythonhosted.org/packages/ff/77/e62aebd343238863f2c9f080ad2ef6ace25c919c6ab383436b5b81cbeef7/pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466", size = 25283133, upload_time = "2025-02-18T18:52:54.549Z" }, + { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749, upload_time = "2025-02-18T18:53:00.062Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007, upload_time = "2025-02-18T18:53:06.581Z" }, + { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566, upload_time = "2025-02-18T18:53:11.958Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991, upload_time = "2025-02-18T18:53:17.678Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986, upload_time = "2025-02-18T18:53:26.263Z" }, + { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026, upload_time = "2025-02-18T18:53:33.063Z" }, + { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108, upload_time = "2025-02-18T18:53:38.462Z" }, ] [[package]] name = "pyaudio" version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/1d/8878c7752febb0f6716a7e1a52cb92ac98871c5aa522cba181878091607c/PyAudio-0.2.14.tar.gz", hash = "sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87", size = 47066 } +sdist = { url = "https://files.pythonhosted.org/packages/26/1d/8878c7752febb0f6716a7e1a52cb92ac98871c5aa522cba181878091607c/PyAudio-0.2.14.tar.gz", hash = "sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87", size = 47066, upload_time = "2023-11-07T07:11:48.806Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/f0/b0eab89eafa70a86b7b566a4df2f94c7880a2d483aa8de1c77d335335b5b/PyAudio-0.2.14-cp311-cp311-win32.whl", hash = "sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289", size = 144624 }, - { url = "https://files.pythonhosted.org/packages/82/d8/f043c854aad450a76e476b0cf9cda1956419e1dacf1062eb9df3c0055abe/PyAudio-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903", size = 164070 }, - { url = "https://files.pythonhosted.org/packages/8d/45/8d2b76e8f6db783f9326c1305f3f816d4a12c8eda5edc6a2e1d03c097c3b/PyAudio-0.2.14-cp312-cp312-win32.whl", hash = "sha256:5fce4bcdd2e0e8c063d835dbe2860dac46437506af509353c7f8114d4bacbd5b", size = 144750 }, - { url = "https://files.pythonhosted.org/packages/b0/6a/d25812e5f79f06285767ec607b39149d02aa3b31d50c2269768f48768930/PyAudio-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3", size = 164126 }, + { url = "https://files.pythonhosted.org/packages/7b/f0/b0eab89eafa70a86b7b566a4df2f94c7880a2d483aa8de1c77d335335b5b/PyAudio-0.2.14-cp311-cp311-win32.whl", hash = "sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289", size = 144624, upload_time = "2023-11-07T07:11:36.94Z" }, + { url = "https://files.pythonhosted.org/packages/82/d8/f043c854aad450a76e476b0cf9cda1956419e1dacf1062eb9df3c0055abe/PyAudio-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903", size = 164070, upload_time = "2023-11-07T07:11:38.579Z" }, + { url = "https://files.pythonhosted.org/packages/8d/45/8d2b76e8f6db783f9326c1305f3f816d4a12c8eda5edc6a2e1d03c097c3b/PyAudio-0.2.14-cp312-cp312-win32.whl", hash = "sha256:5fce4bcdd2e0e8c063d835dbe2860dac46437506af509353c7f8114d4bacbd5b", size = 144750, upload_time = "2023-11-07T07:11:40.142Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/d25812e5f79f06285767ec607b39149d02aa3b31d50c2269768f48768930/PyAudio-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3", size = 164126, upload_time = "2023-11-07T07:11:41.539Z" }, ] [[package]] @@ -1671,69 +1678,69 @@ dependencies = [ { name = "python3-xlib", marker = "sys_platform == 'linux'" }, { name = "pytweening" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236 } +sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236, upload_time = "2023-05-24T20:11:32.972Z" } [[package]] name = "pycapnp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848 } +sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848, upload_time = "2024-04-12T15:35:44.019Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673 }, - { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351 }, - { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666 }, - { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434 }, - { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923 }, - { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105 }, - { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172 }, - { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718 }, - { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714 }, - { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896 }, - { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823 }, - { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564 }, - { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268 }, - { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758 }, - { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816 }, - { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892 }, - { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960 }, - { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780 }, - { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068 }, - { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917 }, - { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169 }, - { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744 }, - { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113 }, - { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055 }, - { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743 }, - { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076 }, - { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361 }, - { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121 }, + { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673, upload_time = "2024-04-12T15:33:32.211Z" }, + { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351, upload_time = "2024-04-12T15:33:35.156Z" }, + { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666, upload_time = "2024-04-12T15:33:37.798Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434, upload_time = "2024-04-12T15:33:40.362Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923, upload_time = "2024-04-12T15:33:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105, upload_time = "2024-04-12T15:33:44.294Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172, upload_time = "2024-04-12T15:33:46.954Z" }, + { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718, upload_time = "2024-04-12T15:33:49.587Z" }, + { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714, upload_time = "2024-04-12T15:33:51.518Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896, upload_time = "2024-04-12T15:33:53.716Z" }, + { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823, upload_time = "2024-04-12T15:33:55.573Z" }, + { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564, upload_time = "2024-04-12T15:33:59.112Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268, upload_time = "2024-04-12T15:34:00.933Z" }, + { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758, upload_time = "2024-04-12T15:34:02.607Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816, upload_time = "2024-04-12T15:34:04.428Z" }, + { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892, upload_time = "2024-04-12T15:34:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960, upload_time = "2024-04-12T15:34:08.771Z" }, + { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780, upload_time = "2024-04-12T15:34:10.863Z" }, + { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068, upload_time = "2024-04-12T15:34:13.543Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917, upload_time = "2024-04-12T15:34:15.636Z" }, + { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169, upload_time = "2024-04-12T15:34:17.758Z" }, + { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744, upload_time = "2024-04-12T15:34:20.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113, upload_time = "2024-04-12T15:34:23.173Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055, upload_time = "2024-04-12T15:34:25.799Z" }, + { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743, upload_time = "2024-04-12T15:34:28.134Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076, upload_time = "2024-04-12T15:34:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361, upload_time = "2024-04-12T15:34:32.25Z" }, + { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121, upload_time = "2024-04-12T15:34:34.208Z" }, ] [[package]] name = "pycparser" version = "2.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload_time = "2024-03-30T13:22:22.564Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload_time = "2024-03-30T13:22:20.476Z" }, ] [[package]] name = "pycryptodome" version = "3.22.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/e6/099310419df5ada522ff34ffc2f1a48a11b37fc6a76f51a6854c182dbd3e/pycryptodome-3.22.0.tar.gz", hash = "sha256:fd7ab568b3ad7b77c908d7c3f7e167ec5a8f035c64ff74f10d47a4edd043d723", size = 4917300 } +sdist = { url = "https://files.pythonhosted.org/packages/44/e6/099310419df5ada522ff34ffc2f1a48a11b37fc6a76f51a6854c182dbd3e/pycryptodome-3.22.0.tar.gz", hash = "sha256:fd7ab568b3ad7b77c908d7c3f7e167ec5a8f035c64ff74f10d47a4edd043d723", size = 4917300, upload_time = "2025-03-15T23:03:36.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/65/a05831c3e4bcd1bf6c2a034e399f74b3d6f30bb4e37e36b9c310c09dc8c0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:009e1c80eea42401a5bd5983c4bab8d516aef22e014a4705622e24e6d9d703c6", size = 2490637 }, - { url = "https://files.pythonhosted.org/packages/5c/76/ff3c2e7a60d17c080c4c6120ebaf60f38717cd387e77f84da4dcf7f64ff0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3b76fa80daeff9519d7e9f6d9e40708f2fce36b9295a847f00624a08293f4f00", size = 1635372 }, - { url = "https://files.pythonhosted.org/packages/cc/7f/cc5d6da0dbc36acd978d80a72b228e33aadaec9c4f91c93221166d8bdc05/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a31fa5914b255ab62aac9265654292ce0404f6b66540a065f538466474baedbc", size = 2177456 }, - { url = "https://files.pythonhosted.org/packages/92/65/35f5063e68790602d892ad36e35ac723147232a9084d1999630045c34593/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0092fd476701eeeb04df5cc509d8b739fa381583cda6a46ff0a60639b7cd70d", size = 2263744 }, - { url = "https://files.pythonhosted.org/packages/cc/67/46acdd35b1081c3dbc72dc466b1b95b80d2f64cad3520f994a9b6c5c7d00/pycryptodome-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d5b0ddc7cf69231736d778bd3ae2b3efb681ae33b64b0c92fb4626bb48bb89", size = 2303356 }, - { url = "https://files.pythonhosted.org/packages/3d/f9/a4f8a83384626098e3f55664519bec113002b9ef751887086ae63a53135a/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f6cf6aa36fcf463e622d2165a5ad9963b2762bebae2f632d719dfb8544903cf5", size = 2176714 }, - { url = "https://files.pythonhosted.org/packages/88/65/e5f8c3a885f70a6e05c84844cd5542120576f4369158946e8cfc623a464d/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec7b40a7ea5af7c40f8837adf20a137d5e11a6eb202cde7e588a48fb2d871a8", size = 2337329 }, - { url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255 }, - { url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403 }, - { url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568 }, + { url = "https://files.pythonhosted.org/packages/1f/65/a05831c3e4bcd1bf6c2a034e399f74b3d6f30bb4e37e36b9c310c09dc8c0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:009e1c80eea42401a5bd5983c4bab8d516aef22e014a4705622e24e6d9d703c6", size = 2490637, upload_time = "2025-03-15T23:02:43.111Z" }, + { url = "https://files.pythonhosted.org/packages/5c/76/ff3c2e7a60d17c080c4c6120ebaf60f38717cd387e77f84da4dcf7f64ff0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3b76fa80daeff9519d7e9f6d9e40708f2fce36b9295a847f00624a08293f4f00", size = 1635372, upload_time = "2025-03-15T23:02:45.564Z" }, + { url = "https://files.pythonhosted.org/packages/cc/7f/cc5d6da0dbc36acd978d80a72b228e33aadaec9c4f91c93221166d8bdc05/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a31fa5914b255ab62aac9265654292ce0404f6b66540a065f538466474baedbc", size = 2177456, upload_time = "2025-03-15T23:02:47.688Z" }, + { url = "https://files.pythonhosted.org/packages/92/65/35f5063e68790602d892ad36e35ac723147232a9084d1999630045c34593/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0092fd476701eeeb04df5cc509d8b739fa381583cda6a46ff0a60639b7cd70d", size = 2263744, upload_time = "2025-03-15T23:02:49.548Z" }, + { url = "https://files.pythonhosted.org/packages/cc/67/46acdd35b1081c3dbc72dc466b1b95b80d2f64cad3520f994a9b6c5c7d00/pycryptodome-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d5b0ddc7cf69231736d778bd3ae2b3efb681ae33b64b0c92fb4626bb48bb89", size = 2303356, upload_time = "2025-03-15T23:02:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/a4f8a83384626098e3f55664519bec113002b9ef751887086ae63a53135a/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f6cf6aa36fcf463e622d2165a5ad9963b2762bebae2f632d719dfb8544903cf5", size = 2176714, upload_time = "2025-03-15T23:02:53.85Z" }, + { url = "https://files.pythonhosted.org/packages/88/65/e5f8c3a885f70a6e05c84844cd5542120576f4369158946e8cfc623a464d/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec7b40a7ea5af7c40f8837adf20a137d5e11a6eb202cde7e588a48fb2d871a8", size = 2337329, upload_time = "2025-03-15T23:02:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255, upload_time = "2025-03-15T23:02:58.055Z" }, + { url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403, upload_time = "2025-03-15T23:03:00.616Z" }, + { url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568, upload_time = "2025-03-15T23:03:03.189Z" }, ] [[package]] @@ -1743,31 +1750,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250 } +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload_time = "2025-03-17T18:53:15.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730 }, + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload_time = "2025-03-17T18:53:14.532Z" }, ] [[package]] name = "pygame" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/cc/08bba60f00541f62aaa252ce0cfbd60aebd04616c0b9574f755b583e45ae/pygame-2.6.1.tar.gz", hash = "sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f", size = 14808125 } +sdist = { url = "https://files.pythonhosted.org/packages/49/cc/08bba60f00541f62aaa252ce0cfbd60aebd04616c0b9574f755b583e45ae/pygame-2.6.1.tar.gz", hash = "sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f", size = 14808125, upload_time = "2024-09-29T13:41:34.698Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ca/8f367cb9fe734c4f6f6400e045593beea2635cd736158f9fabf58ee14e3c/pygame-2.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:20349195326a5e82a16e351ed93465a7845a7e2a9af55b7bc1b2110ea3e344e1", size = 13113753 }, - { url = "https://files.pythonhosted.org/packages/83/47/6edf2f890139616b3219be9cfcc8f0cb8f42eb15efd59597927e390538cb/pygame-2.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3935459109da4bb0b3901da9904f0a3e52028a3332a355d298b1673a334cf21", size = 12378146 }, - { url = "https://files.pythonhosted.org/packages/00/9e/0d8aa8cf93db2d2ee38ebaf1c7b61d0df36ded27eb726221719c150c673d/pygame-2.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31dbdb5d0217f32764797d21c2752e258e5fb7e895326538d82b5f75a0cd856", size = 13611760 }, - { url = "https://files.pythonhosted.org/packages/d7/9e/d06adaa5cc65876bcd7a24f59f67e07f7e4194e6298130024ed3fb22c456/pygame-2.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:173badf82fa198e6888017bea40f511cb28e69ecdd5a72b214e81e4dcd66c3b1", size = 14298054 }, - { url = "https://files.pythonhosted.org/packages/7a/a1/9ae2852ebd3a7cc7d9ae7ff7919ab983e4a5c1b7a14e840732f23b2b48f6/pygame-2.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce8cc108b92de9b149b344ad2e25eedbe773af0dc41dfb24d1f07f679b558c60", size = 13977107 }, - { url = "https://files.pythonhosted.org/packages/31/df/6788fd2e9a864d0496a77670e44a7c012184b7a5382866ab0e60c55c0f28/pygame-2.6.1-cp311-cp311-win32.whl", hash = "sha256:811e7b925146d8149d79193652cbb83e0eca0aae66476b1cb310f0f4226b8b5c", size = 10250863 }, - { url = "https://files.pythonhosted.org/packages/d2/55/ca3eb851aeef4f6f2e98a360c201f0d00bd1ba2eb98e2c7850d80aabc526/pygame-2.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:91476902426facd4bb0dad4dc3b2573bc82c95c71b135e0daaea072ed528d299", size = 10622016 }, - { url = "https://files.pythonhosted.org/packages/92/16/2c602c332f45ff9526d61f6bd764db5096ff9035433e2172e2d2cadae8db/pygame-2.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4ee7f2771f588c966fa2fa8b829be26698c9b4836f82ede5e4edc1a68594942e", size = 13118279 }, - { url = "https://files.pythonhosted.org/packages/cd/53/77ccbc384b251c6e34bfd2e734c638233922449a7844e3c7a11ef91cee39/pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8040ea2ab18c6b255af706ec01355c8a6b08dc48d77fd4ee783f8fc46a843bf", size = 12384524 }, - { url = "https://files.pythonhosted.org/packages/06/be/3ed337583f010696c3b3435e89a74fb29d0c74d0931e8f33c0a4246307a9/pygame-2.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47a6938de93fa610accd4969e638c2aebcb29b2fca518a84c3a39d91ab47116", size = 13587123 }, - { url = "https://files.pythonhosted.org/packages/fd/ca/b015586a450db59313535662991b34d24c1f0c0dc149cc5f496573900f4e/pygame-2.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33006f784e1c7d7e466fcb61d5489da59cc5f7eb098712f792a225df1d4e229d", size = 14275532 }, - { url = "https://files.pythonhosted.org/packages/b9/f2/d31e6ad42d657af07be2ffd779190353f759a07b51232b9e1d724f2cda46/pygame-2.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1206125f14cae22c44565c9d333607f1d9f59487b1f1432945dfc809aeaa3e88", size = 13952653 }, - { url = "https://files.pythonhosted.org/packages/f3/42/8ea2a6979e6fa971702fece1747e862e2256d4a8558fe0da6364dd946c53/pygame-2.6.1-cp312-cp312-win32.whl", hash = "sha256:84fc4054e25262140d09d39e094f6880d730199710829902f0d8ceae0213379e", size = 10252421 }, - { url = "https://files.pythonhosted.org/packages/5f/90/7d766d54bb95939725e9a9361f9c06b0cfbe3fe100aa35400f0a461a278a/pygame-2.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65", size = 10624591 }, + { url = "https://files.pythonhosted.org/packages/c4/ca/8f367cb9fe734c4f6f6400e045593beea2635cd736158f9fabf58ee14e3c/pygame-2.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:20349195326a5e82a16e351ed93465a7845a7e2a9af55b7bc1b2110ea3e344e1", size = 13113753, upload_time = "2024-09-29T14:26:13.751Z" }, + { url = "https://files.pythonhosted.org/packages/83/47/6edf2f890139616b3219be9cfcc8f0cb8f42eb15efd59597927e390538cb/pygame-2.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3935459109da4bb0b3901da9904f0a3e52028a3332a355d298b1673a334cf21", size = 12378146, upload_time = "2024-09-29T14:26:22.456Z" }, + { url = "https://files.pythonhosted.org/packages/00/9e/0d8aa8cf93db2d2ee38ebaf1c7b61d0df36ded27eb726221719c150c673d/pygame-2.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31dbdb5d0217f32764797d21c2752e258e5fb7e895326538d82b5f75a0cd856", size = 13611760, upload_time = "2024-09-29T11:10:47.317Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9e/d06adaa5cc65876bcd7a24f59f67e07f7e4194e6298130024ed3fb22c456/pygame-2.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:173badf82fa198e6888017bea40f511cb28e69ecdd5a72b214e81e4dcd66c3b1", size = 14298054, upload_time = "2024-09-29T11:39:53.891Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a1/9ae2852ebd3a7cc7d9ae7ff7919ab983e4a5c1b7a14e840732f23b2b48f6/pygame-2.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce8cc108b92de9b149b344ad2e25eedbe773af0dc41dfb24d1f07f679b558c60", size = 13977107, upload_time = "2024-09-29T11:39:56.831Z" }, + { url = "https://files.pythonhosted.org/packages/31/df/6788fd2e9a864d0496a77670e44a7c012184b7a5382866ab0e60c55c0f28/pygame-2.6.1-cp311-cp311-win32.whl", hash = "sha256:811e7b925146d8149d79193652cbb83e0eca0aae66476b1cb310f0f4226b8b5c", size = 10250863, upload_time = "2024-09-29T11:44:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/d2/55/ca3eb851aeef4f6f2e98a360c201f0d00bd1ba2eb98e2c7850d80aabc526/pygame-2.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:91476902426facd4bb0dad4dc3b2573bc82c95c71b135e0daaea072ed528d299", size = 10622016, upload_time = "2024-09-29T12:17:01.545Z" }, + { url = "https://files.pythonhosted.org/packages/92/16/2c602c332f45ff9526d61f6bd764db5096ff9035433e2172e2d2cadae8db/pygame-2.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4ee7f2771f588c966fa2fa8b829be26698c9b4836f82ede5e4edc1a68594942e", size = 13118279, upload_time = "2024-09-29T14:26:30.427Z" }, + { url = "https://files.pythonhosted.org/packages/cd/53/77ccbc384b251c6e34bfd2e734c638233922449a7844e3c7a11ef91cee39/pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8040ea2ab18c6b255af706ec01355c8a6b08dc48d77fd4ee783f8fc46a843bf", size = 12384524, upload_time = "2024-09-29T14:26:49.996Z" }, + { url = "https://files.pythonhosted.org/packages/06/be/3ed337583f010696c3b3435e89a74fb29d0c74d0931e8f33c0a4246307a9/pygame-2.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47a6938de93fa610accd4969e638c2aebcb29b2fca518a84c3a39d91ab47116", size = 13587123, upload_time = "2024-09-29T11:10:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/b015586a450db59313535662991b34d24c1f0c0dc149cc5f496573900f4e/pygame-2.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33006f784e1c7d7e466fcb61d5489da59cc5f7eb098712f792a225df1d4e229d", size = 14275532, upload_time = "2024-09-29T11:39:59.356Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f2/d31e6ad42d657af07be2ffd779190353f759a07b51232b9e1d724f2cda46/pygame-2.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1206125f14cae22c44565c9d333607f1d9f59487b1f1432945dfc809aeaa3e88", size = 13952653, upload_time = "2024-09-29T11:40:01.781Z" }, + { url = "https://files.pythonhosted.org/packages/f3/42/8ea2a6979e6fa971702fece1747e862e2256d4a8558fe0da6364dd946c53/pygame-2.6.1-cp312-cp312-win32.whl", hash = "sha256:84fc4054e25262140d09d39e094f6880d730199710829902f0d8ceae0213379e", size = 10252421, upload_time = "2024-09-29T11:14:26.877Z" }, + { url = "https://files.pythonhosted.org/packages/5f/90/7d766d54bb95939725e9a9361f9c06b0cfbe3fe100aa35400f0a461a278a/pygame-2.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65", size = 10624591, upload_time = "2024-09-29T11:52:54.489Z" }, ] [[package]] @@ -1777,24 +1784,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyrect" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699, upload_time = "2020-10-04T02:12:50.806Z" } [[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload_time = "2025-01-06T17:26:30.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload_time = "2025-01-06T17:26:25.553Z" }, ] [[package]] name = "pyjwt" version = "2.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload_time = "2024-11-28T03:43:29.933Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload_time = "2024-11-28T03:43:27.893Z" }, ] [package.optional-dependencies] @@ -1809,18 +1816,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/c8/a59e61f5dd655f5f21033bd643dd31fe980a537ed6f373cdfb49d3a3bd32/pylibsrtp-0.12.0.tar.gz", hash = "sha256:f5c3c0fb6954e7bb74dc7e6398352740ca67327e6759a199fe852dbc7b84b8ac", size = 10878 } +sdist = { url = "https://files.pythonhosted.org/packages/54/c8/a59e61f5dd655f5f21033bd643dd31fe980a537ed6f373cdfb49d3a3bd32/pylibsrtp-0.12.0.tar.gz", hash = "sha256:f5c3c0fb6954e7bb74dc7e6398352740ca67327e6759a199fe852dbc7b84b8ac", size = 10878, upload_time = "2025-04-06T12:35:51.804Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f0/b818395c4cae2d5cc5a0c78fc47d694eae78e6a0d678baeb52a381a26327/pylibsrtp-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5adde3cf9a5feef561d0eb7ed99dedb30b9bf1ce9a0c1770b2bf19fd0b98bc9a", size = 1727918 }, - { url = "https://files.pythonhosted.org/packages/05/1a/ee553abe4431b7bd9bab18f078c0ad2298b94ea55e664da6ecb8700b1052/pylibsrtp-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d2c81d152606721331ece87c80ed17159ba6da55c7c61a6b750cff67ab7f63a5", size = 2057900 }, - { url = "https://files.pythonhosted.org/packages/7f/a2/2dd0188be58d3cba48c5eb4b3c787e5743c111cd0c9289de4b6f2798382a/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:242fa3d44219846bf1734d5df595563a2c8fbb0fb00ccc79ab0f569fc0af2c1b", size = 2567047 }, - { url = "https://files.pythonhosted.org/packages/6c/3a/4bdab9fc1d78f2efa02c8a8f3e9c187bfa278e89481b5123f07c8dd69310/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74aaf8fac1b119a3c762f54751c3d20e77227b84c26d85aae57c2c43129b49c", size = 2168775 }, - { url = "https://files.pythonhosted.org/packages/d0/fc/0b1e1bfed420d79427d50aff84c370dcd78d81af9500c1e86fbcc5bf95e1/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e3e223102989b71f07e1deeb804170ed53fb4e1b283762eb031bd45bb425d4", size = 2225033 }, - { url = "https://files.pythonhosted.org/packages/39/7b/e1021d27900315c2c077ec7d45f50274cedbdde067ff679d44df06f01a8a/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:36d07de64dbc82dbbb99fd77f36c8e23d6730bdbcccf09701945690a9a9a422a", size = 2606093 }, - { url = "https://files.pythonhosted.org/packages/eb/c2/0fae6687a06fcde210a778148ec808af49e431c36fe9908503a695c35479/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ef03b4578577690f716fd023daed8914eee6de9a764fa128eda19a0e645cc032", size = 2193213 }, - { url = "https://files.pythonhosted.org/packages/67/c2/2ed7a4a5c38b999fd34298f76b93d29f5ba8c06f85cfad3efd9468343715/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a8421e9fe4d20ce48d439430e55149f12b1bca1b0436741972c362c49948c0a", size = 2256774 }, - { url = "https://files.pythonhosted.org/packages/48/d7/f13fedce3b21d24f6f154d1dee7287464a34728dcb3b0c50f687dbad5765/pylibsrtp-0.12.0-cp39-abi3-win32.whl", hash = "sha256:cbc9bfbfb2597e993a1aa16b832ba16a9dd4647f70815421bb78484f8b50b924", size = 1156186 }, - { url = "https://files.pythonhosted.org/packages/9b/26/3a20b638a3a3995368f856eeb10701dd6c0e9ace9fb6665eeb1b95ccce19/pylibsrtp-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:061ef1dbb5f08079ac6d7515b7e67ca48a3163e16e5b820beea6b01cb31d7e54", size = 1485072 }, + { url = "https://files.pythonhosted.org/packages/65/f0/b818395c4cae2d5cc5a0c78fc47d694eae78e6a0d678baeb52a381a26327/pylibsrtp-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5adde3cf9a5feef561d0eb7ed99dedb30b9bf1ce9a0c1770b2bf19fd0b98bc9a", size = 1727918, upload_time = "2025-04-06T12:35:36.456Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/ee553abe4431b7bd9bab18f078c0ad2298b94ea55e664da6ecb8700b1052/pylibsrtp-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d2c81d152606721331ece87c80ed17159ba6da55c7c61a6b750cff67ab7f63a5", size = 2057900, upload_time = "2025-04-06T12:35:38.253Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a2/2dd0188be58d3cba48c5eb4b3c787e5743c111cd0c9289de4b6f2798382a/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:242fa3d44219846bf1734d5df595563a2c8fbb0fb00ccc79ab0f569fc0af2c1b", size = 2567047, upload_time = "2025-04-06T12:35:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3a/4bdab9fc1d78f2efa02c8a8f3e9c187bfa278e89481b5123f07c8dd69310/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74aaf8fac1b119a3c762f54751c3d20e77227b84c26d85aae57c2c43129b49c", size = 2168775, upload_time = "2025-04-06T12:35:41.422Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fc/0b1e1bfed420d79427d50aff84c370dcd78d81af9500c1e86fbcc5bf95e1/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e3e223102989b71f07e1deeb804170ed53fb4e1b283762eb031bd45bb425d4", size = 2225033, upload_time = "2025-04-06T12:35:43.03Z" }, + { url = "https://files.pythonhosted.org/packages/39/7b/e1021d27900315c2c077ec7d45f50274cedbdde067ff679d44df06f01a8a/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:36d07de64dbc82dbbb99fd77f36c8e23d6730bdbcccf09701945690a9a9a422a", size = 2606093, upload_time = "2025-04-06T12:35:44.587Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c2/0fae6687a06fcde210a778148ec808af49e431c36fe9908503a695c35479/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ef03b4578577690f716fd023daed8914eee6de9a764fa128eda19a0e645cc032", size = 2193213, upload_time = "2025-04-06T12:35:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/67/c2/2ed7a4a5c38b999fd34298f76b93d29f5ba8c06f85cfad3efd9468343715/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a8421e9fe4d20ce48d439430e55149f12b1bca1b0436741972c362c49948c0a", size = 2256774, upload_time = "2025-04-06T12:35:47.704Z" }, + { url = "https://files.pythonhosted.org/packages/48/d7/f13fedce3b21d24f6f154d1dee7287464a34728dcb3b0c50f687dbad5765/pylibsrtp-0.12.0-cp39-abi3-win32.whl", hash = "sha256:cbc9bfbfb2597e993a1aa16b832ba16a9dd4647f70815421bb78484f8b50b924", size = 1156186, upload_time = "2025-04-06T12:35:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/9b/26/3a20b638a3a3995368f856eeb10701dd6c0e9ace9fb6665eeb1b95ccce19/pylibsrtp-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:061ef1dbb5f08079ac6d7515b7e67ca48a3163e16e5b820beea6b01cb31d7e54", size = 1485072, upload_time = "2025-04-06T12:35:50.312Z" }, ] [[package]] @@ -1835,14 +1842,14 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/13/076a20da28b82be281f7e43e16d9da0f545090f5d14b2125699232b9feba/PyMonCtl-0.92-py3-none-any.whl", hash = "sha256:2495d8dab78f9a7dbce37e74543e60b8bd404a35c3108935697dda7768611b5a", size = 45945 }, + { url = "https://files.pythonhosted.org/packages/2d/13/076a20da28b82be281f7e43e16d9da0f545090f5d14b2125699232b9feba/PyMonCtl-0.92-py3-none-any.whl", hash = "sha256:2495d8dab78f9a7dbce37e74543e60b8bd404a35c3108935697dda7768611b5a", size = 45945, upload_time = "2024-04-22T10:07:09.566Z" }, ] [[package]] name = "pymsgbox" version = "1.0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829, upload_time = "2020-10-11T01:51:43.227Z" } [[package]] name = "pyobjc" @@ -2006,19 +2013,19 @@ dependencies = [ { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0'" }, { name = "pyobjc-framework-webkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044, upload_time = "2025-01-14T19:02:12.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/55/d0971bccf8a5a347eaccf8caa4718766a68281baab83d2b5e211b2767504/pyobjc-11.0-py3-none-any.whl", hash = "sha256:3ed5e4e993192fd7fadd42a4148d266a3587af7453ea3b240bab724d02e34e64", size = 4169 }, + { url = "https://files.pythonhosted.org/packages/18/55/d0971bccf8a5a347eaccf8caa4718766a68281baab83d2b5e211b2767504/pyobjc-11.0-py3-none-any.whl", hash = "sha256:3ed5e4e993192fd7fadd42a4148d266a3587af7453ea3b240bab724d02e34e64", size = 4169, upload_time = "2025-01-14T18:46:44.385Z" }, ] [[package]] name = "pyobjc-core" version = "11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/94/a111239b98260869780a5767e5d74bfd3a8c13a40457f479c28dcd91f89d/pyobjc_core-11.0.tar.gz", hash = "sha256:63bced211cb8a8fb5c8ff46473603da30e51112861bd02c438fbbbc8578d9a70", size = 994931 } +sdist = { url = "https://files.pythonhosted.org/packages/5c/94/a111239b98260869780a5767e5d74bfd3a8c13a40457f479c28dcd91f89d/pyobjc_core-11.0.tar.gz", hash = "sha256:63bced211cb8a8fb5c8ff46473603da30e51112861bd02c438fbbbc8578d9a70", size = 994931, upload_time = "2025-01-14T19:02:13.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/05/fa97309c3b1bc1ec90d701db89902e0bd5e1024023aa2c5387b889458b1b/pyobjc_core-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50675c0bb8696fe960a28466f9baf6943df2928a1fd85625d678fa2f428bd0bd", size = 727295 }, - { url = "https://files.pythonhosted.org/packages/56/ce/bf3ff9a9347721a398c3dfb83e29b43fb166b7ef590f3f7b7ddcd283df39/pyobjc_core-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a03061d4955c62ddd7754224a80cdadfdf17b6b5f60df1d9169a3b1b02923f0b", size = 739750 }, + { url = "https://files.pythonhosted.org/packages/52/05/fa97309c3b1bc1ec90d701db89902e0bd5e1024023aa2c5387b889458b1b/pyobjc_core-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50675c0bb8696fe960a28466f9baf6943df2928a1fd85625d678fa2f428bd0bd", size = 727295, upload_time = "2025-01-14T18:46:50.208Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/bf3ff9a9347721a398c3dfb83e29b43fb166b7ef590f3f7b7ddcd283df39/pyobjc_core-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a03061d4955c62ddd7754224a80cdadfdf17b6b5f60df1d9169a3b1b02923f0b", size = 739750, upload_time = "2025-01-14T18:46:53.039Z" }, ] [[package]] @@ -2030,10 +2037,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597, upload_time = "2025-01-14T19:02:17.596Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/2e/babcd02cd833c0aba34e10c34a2184021b8a3c7cb45d1ae806156c2b519d/pyobjc_framework_Accessibility-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c3a751d17b288bb56a98a10b52b253b3002c885fe686b604788acac1e9739437", size = 10948 }, - { url = "https://files.pythonhosted.org/packages/e8/ea/da3f982eeaffb80efb480892106caa19a2c9c8b8954570837ddbcd983520/pyobjc_framework_Accessibility-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:34536f3d60aeda618b384b1207a8c6f9978de278ce229c3469ef14fd27a3befa", size = 10962 }, + { url = "https://files.pythonhosted.org/packages/52/2e/babcd02cd833c0aba34e10c34a2184021b8a3c7cb45d1ae806156c2b519d/pyobjc_framework_Accessibility-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c3a751d17b288bb56a98a10b52b253b3002c885fe686b604788acac1e9739437", size = 10948, upload_time = "2025-01-14T18:47:34.37Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ea/da3f982eeaffb80efb480892106caa19a2c9c8b8954570837ddbcd983520/pyobjc_framework_Accessibility-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:34536f3d60aeda618b384b1207a8c6f9978de278ce229c3469ef14fd27a3befa", size = 10962, upload_time = "2025-01-14T18:47:35.313Z" }, ] [[package]] @@ -2044,10 +2051,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340 } +sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340, upload_time = "2025-01-14T19:02:18.625Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/45/5dfc72c82087d458ce7ddb17a338a38ae1848e72620537f31ed97192c65e/pyobjc_framework_Accounts-11.0-py2.py3-none-any.whl", hash = "sha256:3e4b494e1158e3250e4b4a09e9ff33b38f82d31aefe50dd47152c4a20ecdeec4", size = 5035 }, - { url = "https://files.pythonhosted.org/packages/96/96/39b0cc9ced1180a93c75924a06598f24d0a7554b3e8ddfcb0828c0957476/pyobjc_framework_Accounts-11.0-py3-none-any.whl", hash = "sha256:ad0e378bd07ca7c88b45cda63b85424bc344e81ea44c0ae7327872d91cad311a", size = 5104 }, + { url = "https://files.pythonhosted.org/packages/93/45/5dfc72c82087d458ce7ddb17a338a38ae1848e72620537f31ed97192c65e/pyobjc_framework_Accounts-11.0-py2.py3-none-any.whl", hash = "sha256:3e4b494e1158e3250e4b4a09e9ff33b38f82d31aefe50dd47152c4a20ecdeec4", size = 5035, upload_time = "2025-01-14T18:47:40.92Z" }, + { url = "https://files.pythonhosted.org/packages/96/96/39b0cc9ced1180a93c75924a06598f24d0a7554b3e8ddfcb0828c0957476/pyobjc_framework_Accounts-11.0-py3-none-any.whl", hash = "sha256:ad0e378bd07ca7c88b45cda63b85424bc344e81ea44c0ae7327872d91cad311a", size = 5104, upload_time = "2025-01-14T18:47:41.967Z" }, ] [[package]] @@ -2058,10 +2065,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116 } +sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116, upload_time = "2025-01-14T19:02:19.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/7a/8b874a52ff57dad999330ac1f899e6df8e35cec2cad8a90d8002d3c5f196/pyobjc_framework_AddressBook-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:af7de23aac7571a3b9dad5b2881d8f186653aa72903db8d7dbfd2c7b993156b9", size = 13010 }, - { url = "https://files.pythonhosted.org/packages/0c/b4/93de1195c22cbaf4996aeb6d55e79fc7d76311cacfe8fd716c70fb20e391/pyobjc_framework_AddressBook-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b634ef80920ab9208f2937527e4a498e7afa6e2ceb639ebb483387ab5b9accc", size = 13039 }, + { url = "https://files.pythonhosted.org/packages/2d/7a/8b874a52ff57dad999330ac1f899e6df8e35cec2cad8a90d8002d3c5f196/pyobjc_framework_AddressBook-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:af7de23aac7571a3b9dad5b2881d8f186653aa72903db8d7dbfd2c7b993156b9", size = 13010, upload_time = "2025-01-14T18:47:47.925Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b4/93de1195c22cbaf4996aeb6d55e79fc7d76311cacfe8fd716c70fb20e391/pyobjc_framework_AddressBook-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b634ef80920ab9208f2937527e4a498e7afa6e2ceb639ebb483387ab5b9accc", size = 13039, upload_time = "2025-01-14T18:47:49.509Z" }, ] [[package]] @@ -2072,10 +2079,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732 } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732, upload_time = "2025-01-14T19:02:20.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/10/601c9f5a07450ce75e166042d9ac5efe6286ac2d15212885a920260af9e3/pyobjc_framework_AdServices-11.0-py2.py3-none-any.whl", hash = "sha256:7cd1458f60175cd46bd88061c20e82f04b2576fc00bc5d54d67c18dcb870e27f", size = 3420 }, - { url = "https://files.pythonhosted.org/packages/89/40/98a9116790e163d6c9ac0d19ce66307b03f9ac5ee64631db69899457b154/pyobjc_framework_AdServices-11.0-py3-none-any.whl", hash = "sha256:6426d4e4a43f5ee5ce7bab44d85647dbded3e17c0c62d8923cebaf927c4162ca", size = 3486 }, + { url = "https://files.pythonhosted.org/packages/1d/10/601c9f5a07450ce75e166042d9ac5efe6286ac2d15212885a920260af9e3/pyobjc_framework_AdServices-11.0-py2.py3-none-any.whl", hash = "sha256:7cd1458f60175cd46bd88061c20e82f04b2576fc00bc5d54d67c18dcb870e27f", size = 3420, upload_time = "2025-01-14T18:47:42.812Z" }, + { url = "https://files.pythonhosted.org/packages/89/40/98a9116790e163d6c9ac0d19ce66307b03f9ac5ee64631db69899457b154/pyobjc_framework_AdServices-11.0-py3-none-any.whl", hash = "sha256:6426d4e4a43f5ee5ce7bab44d85647dbded3e17c0c62d8923cebaf927c4162ca", size = 3486, upload_time = "2025-01-14T18:47:43.845Z" }, ] [[package]] @@ -2086,10 +2093,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819 } +sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819, upload_time = "2025-01-14T19:02:23.032Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/7f/2023c0a973f8823175c7e409fdbd306b275b0bb2723acf12ffade6ba5dbe/pyobjc_framework_AdSupport-11.0-py2.py3-none-any.whl", hash = "sha256:59161f5046def176d3aa6fdd6a05916029ca69ac69f836c67e0dd780a5efcf0f", size = 3334 }, - { url = "https://files.pythonhosted.org/packages/cf/84/26c4275732952416603026888ca5462ed84372d412d0ccd7a1c750c01673/pyobjc_framework_AdSupport-11.0-py3-none-any.whl", hash = "sha256:91ba05eb5602911287bd04b0efefb7a485f9af255095b87c3e77bb7d1d1242ed", size = 3405 }, + { url = "https://files.pythonhosted.org/packages/6f/7f/2023c0a973f8823175c7e409fdbd306b275b0bb2723acf12ffade6ba5dbe/pyobjc_framework_AdSupport-11.0-py2.py3-none-any.whl", hash = "sha256:59161f5046def176d3aa6fdd6a05916029ca69ac69f836c67e0dd780a5efcf0f", size = 3334, upload_time = "2025-01-14T18:47:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/cf/84/26c4275732952416603026888ca5462ed84372d412d0ccd7a1c750c01673/pyobjc_framework_AdSupport-11.0-py3-none-any.whl", hash = "sha256:91ba05eb5602911287bd04b0efefb7a485f9af255095b87c3e77bb7d1d1242ed", size = 3405, upload_time = "2025-01-14T18:47:45.767Z" }, ] [[package]] @@ -2100,10 +2107,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638 } +sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638, upload_time = "2025-01-14T19:02:25.1Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/4b/5e7f6a182129be6f229ee6c036d84359b46b0f5f695824315c47b19d3149/pyobjc_framework_AppleScriptKit-11.0-py2.py3-none-any.whl", hash = "sha256:e8acc5ca99f5123ec4e60cb356c7cc407d5fe533ca53e5fa341b51f65495973b", size = 4246 }, - { url = "https://files.pythonhosted.org/packages/b6/ce/7965604f553c91fbd5602e17057b0935c100542abaf76291921335b6f75c/pyobjc_framework_AppleScriptKit-11.0-py3-none-any.whl", hash = "sha256:92cffd943a4d17f684bb51245744e9d0bb8992b2967125845dfeab09d26fc624", size = 4317 }, + { url = "https://files.pythonhosted.org/packages/97/4b/5e7f6a182129be6f229ee6c036d84359b46b0f5f695824315c47b19d3149/pyobjc_framework_AppleScriptKit-11.0-py2.py3-none-any.whl", hash = "sha256:e8acc5ca99f5123ec4e60cb356c7cc407d5fe533ca53e5fa341b51f65495973b", size = 4246, upload_time = "2025-01-14T18:47:59.508Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ce/7965604f553c91fbd5602e17057b0935c100542abaf76291921335b6f75c/pyobjc_framework_AppleScriptKit-11.0-py3-none-any.whl", hash = "sha256:92cffd943a4d17f684bb51245744e9d0bb8992b2967125845dfeab09d26fc624", size = 4317, upload_time = "2025-01-14T18:48:02.221Z" }, ] [[package]] @@ -2114,10 +2121,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675, upload_time = "2025-01-14T19:02:25.947Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/7d/b3e28759df060f26a31407282e789a1a321612afcee3871134fdac8dc75f/pyobjc_framework_AppleScriptObjC-11.0-py2.py3-none-any.whl", hash = "sha256:a4c8d417fdb64180a283eadf8ddb804ba7f9e3cef149216a11b65e1d3509c55b", size = 4347 }, - { url = "https://files.pythonhosted.org/packages/0d/e7/c080a1cd77ce04e3bf4079a941105d3d670b9ba0fc91a54d4a1764bea02d/pyobjc_framework_AppleScriptObjC-11.0-py3-none-any.whl", hash = "sha256:681006b0cdf0279cd06b6d0f62b542b7f3b3b9b5d2391f7aa3798d8b355d67bf", size = 4416 }, + { url = "https://files.pythonhosted.org/packages/b8/7d/b3e28759df060f26a31407282e789a1a321612afcee3871134fdac8dc75f/pyobjc_framework_AppleScriptObjC-11.0-py2.py3-none-any.whl", hash = "sha256:a4c8d417fdb64180a283eadf8ddb804ba7f9e3cef149216a11b65e1d3509c55b", size = 4347, upload_time = "2025-01-14T18:48:03.193Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e7/c080a1cd77ce04e3bf4079a941105d3d670b9ba0fc91a54d4a1764bea02d/pyobjc_framework_AppleScriptObjC-11.0-py3-none-any.whl", hash = "sha256:681006b0cdf0279cd06b6d0f62b542b7f3b3b9b5d2391f7aa3798d8b355d67bf", size = 4416, upload_time = "2025-01-14T18:48:04.219Z" }, ] [[package]] @@ -2130,10 +2137,10 @@ dependencies = [ { name = "pyobjc-framework-coretext" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334, upload_time = "2025-01-14T19:02:26.828Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/37/3d4dc6c004aaeb67bd43f7261d7c169ff45b8fc0eefbc7ba8cd6b0c881bc/pyobjc_framework_ApplicationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61a99eef23abb704257310db4f5271137707e184768f6407030c01de4731b67b", size = 30846 }, - { url = "https://files.pythonhosted.org/packages/74/a9/7a45a67e126d32c61ea22ffd80e87ff7e05b4acf32bede6cce071fbfffc8/pyobjc_framework_ApplicationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5fbeb425897d6129471d451ec61a29ddd5b1386eb26b1dd49cb313e34616ee21", size = 30908 }, + { url = "https://files.pythonhosted.org/packages/99/37/3d4dc6c004aaeb67bd43f7261d7c169ff45b8fc0eefbc7ba8cd6b0c881bc/pyobjc_framework_ApplicationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61a99eef23abb704257310db4f5271137707e184768f6407030c01de4731b67b", size = 30846, upload_time = "2025-01-14T18:48:06.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/a9/7a45a67e126d32c61ea22ffd80e87ff7e05b4acf32bede6cce071fbfffc8/pyobjc_framework_ApplicationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5fbeb425897d6129471d451ec61a29ddd5b1386eb26b1dd49cb313e34616ee21", size = 30908, upload_time = "2025-01-14T18:48:07.177Z" }, ] [[package]] @@ -2144,10 +2151,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385 } +sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385, upload_time = "2025-01-14T19:02:29.226Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/72/6e460cd763a3048c4d75769ed60a5af7832122b78224f710e40a9eb1c5cf/pyobjc_framework_AppTrackingTransparency-11.0-py2.py3-none-any.whl", hash = "sha256:1bf6d4f148d9f5d5befe90fcfd88ce988458a52719d53d5989b08e4fbed58864", size = 3805 }, - { url = "https://files.pythonhosted.org/packages/33/cb/ef2622ee08349293aae6f81216cfee2423ad37d8a1d14ba4690b537d8850/pyobjc_framework_AppTrackingTransparency-11.0-py3-none-any.whl", hash = "sha256:347f876aea9d9f47d9fbf6dfa6d3f250ecd46f56a7c4616386327061e2ecc4e9", size = 3878 }, + { url = "https://files.pythonhosted.org/packages/c4/72/6e460cd763a3048c4d75769ed60a5af7832122b78224f710e40a9eb1c5cf/pyobjc_framework_AppTrackingTransparency-11.0-py2.py3-none-any.whl", hash = "sha256:1bf6d4f148d9f5d5befe90fcfd88ce988458a52719d53d5989b08e4fbed58864", size = 3805, upload_time = "2025-01-14T18:47:57.492Z" }, + { url = "https://files.pythonhosted.org/packages/33/cb/ef2622ee08349293aae6f81216cfee2423ad37d8a1d14ba4690b537d8850/pyobjc_framework_AppTrackingTransparency-11.0-py3-none-any.whl", hash = "sha256:347f876aea9d9f47d9fbf6dfa6d3f250ecd46f56a7c4616386327061e2ecc4e9", size = 3878, upload_time = "2025-01-14T18:47:58.595Z" }, ] [[package]] @@ -2158,10 +2165,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418 } +sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418, upload_time = "2025-01-14T19:02:30.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/33/2ee33542febb40174d40ae8bbdf672b4e438a3fb41ba6a4d4a3e6800121b/pyobjc_framework_AudioVideoBridging-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d025e49ca6238be96d0a1c22942b548a8d445ef8eb71259b4769e119810f42c6", size = 10944 }, - { url = "https://files.pythonhosted.org/packages/45/ea/db8295e17b0b544b06620e4019afcc76d7b743a8f03cb8a1024b2bc118ac/pyobjc_framework_AudioVideoBridging-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d414ecffeb23cddc8e64262af170e663c93e8d462d18aa7067d4584069967859", size = 10962 }, + { url = "https://files.pythonhosted.org/packages/3c/33/2ee33542febb40174d40ae8bbdf672b4e438a3fb41ba6a4d4a3e6800121b/pyobjc_framework_AudioVideoBridging-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d025e49ca6238be96d0a1c22942b548a8d445ef8eb71259b4769e119810f42c6", size = 10944, upload_time = "2025-01-14T18:48:11.978Z" }, + { url = "https://files.pythonhosted.org/packages/45/ea/db8295e17b0b544b06620e4019afcc76d7b743a8f03cb8a1024b2bc118ac/pyobjc_framework_AudioVideoBridging-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d414ecffeb23cddc8e64262af170e663c93e8d462d18aa7067d4584069967859", size = 10962, upload_time = "2025-01-14T18:48:12.953Z" }, ] [[package]] @@ -2172,10 +2179,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036 } +sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036, upload_time = "2025-01-14T19:02:31.115Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/16/6246cf10e2d245f4018c02f351f8eb4cc93823f6e7e4dd584ab292cda786/pyobjc_framework_AuthenticationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84e3b23478cf8995883acfe6c1a24503c84caf2f8dbe540377fe19fb787ce9b2", size = 20079 }, - { url = "https://files.pythonhosted.org/packages/b7/ca/81a55a0714e73695b536bfbcbf0f5ddf68da9485b468406f6ef887a04938/pyobjc_framework_AuthenticationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1779f72c264f749946fcbfba0575a985c1e297d426617739a533554dbf172f9a", size = 20105 }, + { url = "https://files.pythonhosted.org/packages/45/16/6246cf10e2d245f4018c02f351f8eb4cc93823f6e7e4dd584ab292cda786/pyobjc_framework_AuthenticationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84e3b23478cf8995883acfe6c1a24503c84caf2f8dbe540377fe19fb787ce9b2", size = 20079, upload_time = "2025-01-14T18:48:19.023Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ca/81a55a0714e73695b536bfbcbf0f5ddf68da9485b468406f6ef887a04938/pyobjc_framework_AuthenticationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1779f72c264f749946fcbfba0575a985c1e297d426617739a533554dbf172f9a", size = 20105, upload_time = "2025-01-14T18:48:19.945Z" }, ] [[package]] @@ -2186,10 +2193,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420 } +sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420, upload_time = "2025-01-14T19:02:32.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3f/b593adce2f5b9f9427d784db56d8195adc2cfb340d4d3578914539a17faa/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:25f2db399eb0a47e345d0471c7af930f5a3be899ba6edb40bd9125719e4b526f", size = 9015 }, - { url = "https://files.pythonhosted.org/packages/4e/c3/b6b779d783dcf3667a2011d8af0d801f6639df9735cdc34c6e6b79822298/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b6433452d2c4cdb0eef16cc78a24ba9c61efb5bb04709ee10ca94b69119e889c", size = 9034 }, + { url = "https://files.pythonhosted.org/packages/50/3f/b593adce2f5b9f9427d784db56d8195adc2cfb340d4d3578914539a17faa/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:25f2db399eb0a47e345d0471c7af930f5a3be899ba6edb40bd9125719e4b526f", size = 9015, upload_time = "2025-01-14T18:48:25.686Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c3/b6b779d783dcf3667a2011d8af0d801f6639df9735cdc34c6e6b79822298/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b6433452d2c4cdb0eef16cc78a24ba9c61efb5bb04709ee10ca94b69119e889c", size = 9034, upload_time = "2025-01-14T18:48:26.58Z" }, ] [[package]] @@ -2200,10 +2207,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905 } +sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905, upload_time = "2025-01-14T19:02:33.039Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/2b/bfe673491042849ad400bebf557b8047317757283b98ad9921fbb6f6cae9/pyobjc_framework_Automator-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:850f9641a54cc8d9a3d02c2d87a4e80aed2413b37aa6c26a7046088b77da5b42", size = 9811 }, - { url = "https://files.pythonhosted.org/packages/13/00/e60db832c536fd354fab7e813ee781327358e6bcbc4cacbd9695dade7006/pyobjc_framework_Automator-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eb1b9b16873ec1d2f8af9a04ca1b2fcaa324ce4a1fada0d02fa239f6fecf773b", size = 9827 }, + { url = "https://files.pythonhosted.org/packages/7b/2b/bfe673491042849ad400bebf557b8047317757283b98ad9921fbb6f6cae9/pyobjc_framework_Automator-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:850f9641a54cc8d9a3d02c2d87a4e80aed2413b37aa6c26a7046088b77da5b42", size = 9811, upload_time = "2025-01-14T18:48:31.987Z" }, + { url = "https://files.pythonhosted.org/packages/13/00/e60db832c536fd354fab7e813ee781327358e6bcbc4cacbd9695dade7006/pyobjc_framework_Automator-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eb1b9b16873ec1d2f8af9a04ca1b2fcaa324ce4a1fada0d02fa239f6fecf773b", size = 9827, upload_time = "2025-01-14T18:48:32.958Z" }, ] [[package]] @@ -2217,10 +2224,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064 } +sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064, upload_time = "2025-01-14T19:02:35.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/327654548fa210b4637350de016183fbb1f6f8f9213d2c6c9b492eb8b44c/pyobjc_framework_AVFoundation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87db350311c1d7e07d68036cdde3d01c09d97b8ba502241c0c1699d7a9c6f2e4", size = 71345 }, - { url = "https://files.pythonhosted.org/packages/f5/36/e09b20f280953fa7be95a9266e5ad75f2e8b184cc39260c0537b3e60b534/pyobjc_framework_AVFoundation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6bb6f4be53c0fb42bee3f46cf0bb5396a8fd13f92d47a01f6b77037a1134f26b", size = 71314 }, + { url = "https://files.pythonhosted.org/packages/44/b5/327654548fa210b4637350de016183fbb1f6f8f9213d2c6c9b492eb8b44c/pyobjc_framework_AVFoundation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87db350311c1d7e07d68036cdde3d01c09d97b8ba502241c0c1699d7a9c6f2e4", size = 71345, upload_time = "2025-01-14T18:47:04.589Z" }, + { url = "https://files.pythonhosted.org/packages/f5/36/e09b20f280953fa7be95a9266e5ad75f2e8b184cc39260c0537b3e60b534/pyobjc_framework_AVFoundation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6bb6f4be53c0fb42bee3f46cf0bb5396a8fd13f92d47a01f6b77037a1134f26b", size = 71314, upload_time = "2025-01-14T18:47:05.616Z" }, ] [[package]] @@ -2232,10 +2239,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311 } +sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311, upload_time = "2025-01-14T19:02:37.018Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/ae/aed1023150483a288922c447e1997f4f4e9d0460038e1a070a3a53b85c19/pyobjc_framework_AVKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16b5560860c1e13e692c677ad04d8e194d0b9931dd3f15e3df4dbd7217cc8ab1", size = 11960 }, - { url = "https://files.pythonhosted.org/packages/01/f4/08684e5af2a2e8940e6411e96ef1d7ed1e51a121abb19c93c25c34969213/pyobjc_framework_AVKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f4da468b97bb7f356024e31647619cd1cd435b543e467209da0ee0abdfdc7121", size = 11969 }, + { url = "https://files.pythonhosted.org/packages/d2/ae/aed1023150483a288922c447e1997f4f4e9d0460038e1a070a3a53b85c19/pyobjc_framework_AVKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16b5560860c1e13e692c677ad04d8e194d0b9931dd3f15e3df4dbd7217cc8ab1", size = 11960, upload_time = "2025-01-14T18:47:15.738Z" }, + { url = "https://files.pythonhosted.org/packages/01/f4/08684e5af2a2e8940e6411e96ef1d7ed1e51a121abb19c93c25c34969213/pyobjc_framework_AVKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f4da468b97bb7f356024e31647619cd1cd435b543e467209da0ee0abdfdc7121", size = 11969, upload_time = "2025-01-14T18:47:16.602Z" }, ] [[package]] @@ -2246,10 +2253,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561 } +sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561, upload_time = "2025-01-14T19:02:38.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/9c/ea6924de09e13f858210d6dd934f00773b1e3db6af886c72841ed545560f/pyobjc_framework_AVRouting-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54e58cd0292f734aba035599f37a0c00f03761e9ff5cf53a0857cec7949bb39c", size = 8067 }, - { url = "https://files.pythonhosted.org/packages/3f/92/774e10af5aba5742c4a2dd563fa819550d9caa755d2648b3cc87bbe30129/pyobjc_framework_AVRouting-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:779db3fb0048b22c5dcf5871930025c0fd93068f87946e8053f31a3366fa6fb0", size = 8078 }, + { url = "https://files.pythonhosted.org/packages/8b/9c/ea6924de09e13f858210d6dd934f00773b1e3db6af886c72841ed545560f/pyobjc_framework_AVRouting-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54e58cd0292f734aba035599f37a0c00f03761e9ff5cf53a0857cec7949bb39c", size = 8067, upload_time = "2025-01-14T18:47:26.354Z" }, + { url = "https://files.pythonhosted.org/packages/3f/92/774e10af5aba5742c4a2dd563fa819550d9caa755d2648b3cc87bbe30129/pyobjc_framework_AVRouting-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:779db3fb0048b22c5dcf5871930025c0fd93068f87946e8053f31a3366fa6fb0", size = 8078, upload_time = "2025-01-14T18:47:28.53Z" }, ] [[package]] @@ -2260,10 +2267,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658 } +sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658, upload_time = "2025-01-14T19:02:42.665Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/9d/bea4408649199340ec7ed154bbaa1942a0b0955006b3153088b3f35e6ff6/pyobjc_framework_BackgroundAssets-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:812bcc4eaf71c1cc42e94edc2b5ad0414d16cfe1da5c421edd9382417d625f06", size = 9499 }, - { url = "https://files.pythonhosted.org/packages/bd/79/726c14fd26553c8bbe8b2ed55caa45d89093e2e85b45c1b598dd04ea7589/pyobjc_framework_BackgroundAssets-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:96b3fc40c514867d4a0b3ad4d256bc5134d789e22fa306a6b21e49ecadc51698", size = 9521 }, + { url = "https://files.pythonhosted.org/packages/e9/9d/bea4408649199340ec7ed154bbaa1942a0b0955006b3153088b3f35e6ff6/pyobjc_framework_BackgroundAssets-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:812bcc4eaf71c1cc42e94edc2b5ad0414d16cfe1da5c421edd9382417d625f06", size = 9499, upload_time = "2025-01-14T18:48:39.156Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/726c14fd26553c8bbe8b2ed55caa45d89093e2e85b45c1b598dd04ea7589/pyobjc_framework_BackgroundAssets-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:96b3fc40c514867d4a0b3ad4d256bc5134d789e22fa306a6b21e49ecadc51698", size = 9521, upload_time = "2025-01-14T18:48:40.063Z" }, ] [[package]] @@ -2277,10 +2284,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352 } +sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352, upload_time = "2025-01-14T19:02:45.499Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/6d/6aa929d4993453817523db9c82a4e6e2cce7104fa59e29ee857f9e926b0d/pyobjc_framework_BrowserEngineKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:58494bc3ccc21a63751b7c9f8788d0240c3f1aad84cf221c0e42c9764a069ba4", size = 10913 }, - { url = "https://files.pythonhosted.org/packages/a8/2f/dd18f7ff9438ad4612febfbdb2e4bded37033347b9f0e1355df76f2c5213/pyobjc_framework_BrowserEngineKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0925edfd60a24f53819cfd11f07926fd42bc0fbeb7a4982998a08742e859dbff", size = 10933 }, + { url = "https://files.pythonhosted.org/packages/da/6d/6aa929d4993453817523db9c82a4e6e2cce7104fa59e29ee857f9e926b0d/pyobjc_framework_BrowserEngineKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:58494bc3ccc21a63751b7c9f8788d0240c3f1aad84cf221c0e42c9764a069ba4", size = 10913, upload_time = "2025-01-14T18:48:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/dd18f7ff9438ad4612febfbdb2e4bded37033347b9f0e1355df76f2c5213/pyobjc_framework_BrowserEngineKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0925edfd60a24f53819cfd11f07926fd42bc0fbeb7a4982998a08742e859dbff", size = 10933, upload_time = "2025-01-14T18:48:45.673Z" }, ] [[package]] @@ -2291,10 +2298,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953, upload_time = "2025-01-14T19:02:46.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/5b/d7313368ea4056092400c7a4ed5c705d3d21a443641d98b140054edbd930/pyobjc_framework_BusinessChat-11.0-py2.py3-none-any.whl", hash = "sha256:1f732fdace31d2abdd14b3054f27a5e0f4591c7e1bef069b6aeb4f9c8d9ec487", size = 3408 }, - { url = "https://files.pythonhosted.org/packages/8a/e6/c82e2eb2b4ad4407f1ada6d41ef583eb211cce88ffcc2e05c826760f721d/pyobjc_framework_BusinessChat-11.0-py3-none-any.whl", hash = "sha256:47a2e4da9b061daa89a6367cb0e6bb8cdea0627379dd6d5095a8fd20243d8613", size = 3477 }, + { url = "https://files.pythonhosted.org/packages/d4/5b/d7313368ea4056092400c7a4ed5c705d3d21a443641d98b140054edbd930/pyobjc_framework_BusinessChat-11.0-py2.py3-none-any.whl", hash = "sha256:1f732fdace31d2abdd14b3054f27a5e0f4591c7e1bef069b6aeb4f9c8d9ec487", size = 3408, upload_time = "2025-01-14T18:48:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e6/c82e2eb2b4ad4407f1ada6d41ef583eb211cce88ffcc2e05c826760f721d/pyobjc_framework_BusinessChat-11.0-py3-none-any.whl", hash = "sha256:47a2e4da9b061daa89a6367cb0e6bb8cdea0627379dd6d5095a8fd20243d8613", size = 3477, upload_time = "2025-01-14T18:48:52.723Z" }, ] [[package]] @@ -2305,10 +2312,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475 } +sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475, upload_time = "2025-01-14T19:02:48.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/e1/02bda98aae43957943adb09700265603f8ff8ff2197e57b082237a8e1a8f/pyobjc_framework_CalendarStore-11.0-py2.py3-none-any.whl", hash = "sha256:67ddc18c96bba42118fc92f1117b053c58c8888edb74193f0be67a10051cc9e2", size = 5183 }, - { url = "https://files.pythonhosted.org/packages/a2/5b/922df21b738e8d349df27b2a73eaf8bba93c84c8c4d0d133fdd5de2ff236/pyobjc_framework_CalendarStore-11.0-py3-none-any.whl", hash = "sha256:9b310fe66ac12e0feb7c8e3166034bec357a45f7f8b8916e93eddc6f199d08c8", size = 5251 }, + { url = "https://files.pythonhosted.org/packages/e3/e1/02bda98aae43957943adb09700265603f8ff8ff2197e57b082237a8e1a8f/pyobjc_framework_CalendarStore-11.0-py2.py3-none-any.whl", hash = "sha256:67ddc18c96bba42118fc92f1117b053c58c8888edb74193f0be67a10051cc9e2", size = 5183, upload_time = "2025-01-14T18:49:01.649Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5b/922df21b738e8d349df27b2a73eaf8bba93c84c8c4d0d133fdd5de2ff236/pyobjc_framework_CalendarStore-11.0-py3-none-any.whl", hash = "sha256:9b310fe66ac12e0feb7c8e3166034bec357a45f7f8b8916e93eddc6f199d08c8", size = 5251, upload_time = "2025-01-14T18:49:03.224Z" }, ] [[package]] @@ -2319,10 +2326,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720 } +sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720, upload_time = "2025-01-14T19:02:50.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/86/8d7dc24702ae810b6230d8b2cebb1c31e12abc31507095b1a9655715c921/pyobjc_framework_CallKit-11.0-py2.py3-none-any.whl", hash = "sha256:f19d94b61ecd981f4691fd244f536f947687b872ac793ccc2b3122b3854e887a", size = 5248 }, - { url = "https://files.pythonhosted.org/packages/25/bd/ff89f7e5438c767fc43f603bee42a447315be48a09f64b9aa4da719ecdfc/pyobjc_framework_CallKit-11.0-py3-none-any.whl", hash = "sha256:95394b7f7a50916debe4f7a884ce9135d11733a14e07a8c502171e77bd0087a4", size = 5314 }, + { url = "https://files.pythonhosted.org/packages/22/86/8d7dc24702ae810b6230d8b2cebb1c31e12abc31507095b1a9655715c921/pyobjc_framework_CallKit-11.0-py2.py3-none-any.whl", hash = "sha256:f19d94b61ecd981f4691fd244f536f947687b872ac793ccc2b3122b3854e887a", size = 5248, upload_time = "2025-01-14T18:49:05.438Z" }, + { url = "https://files.pythonhosted.org/packages/25/bd/ff89f7e5438c767fc43f603bee42a447315be48a09f64b9aa4da719ecdfc/pyobjc_framework_CallKit-11.0-py3-none-any.whl", hash = "sha256:95394b7f7a50916debe4f7a884ce9135d11733a14e07a8c502171e77bd0087a4", size = 5314, upload_time = "2025-01-14T18:49:06.459Z" }, ] [[package]] @@ -2333,10 +2340,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538 } +sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538, upload_time = "2025-01-14T19:02:51.62Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/fb/e5724934c3a2bbed4fbda4230e15a8b7b86313b39491876647300cb4fb11/pyobjc_framework_Carbon-11.0-py2.py3-none-any.whl", hash = "sha256:beef5095269d8e5427e09f9687963515c1b79fbf6927ff756a8414445892987d", size = 4700 }, - { url = "https://files.pythonhosted.org/packages/1a/3d/b53c2d8949067f3f45491e250620e437569f1b4e6a028f2f5e721726283e/pyobjc_framework_Carbon-11.0-py3-none-any.whl", hash = "sha256:9a269042e8f5705897ac64d2b48515ba055462c88460cf140f5d8d4b8c806a42", size = 4768 }, + { url = "https://files.pythonhosted.org/packages/93/fb/e5724934c3a2bbed4fbda4230e15a8b7b86313b39491876647300cb4fb11/pyobjc_framework_Carbon-11.0-py2.py3-none-any.whl", hash = "sha256:beef5095269d8e5427e09f9687963515c1b79fbf6927ff756a8414445892987d", size = 4700, upload_time = "2025-01-14T18:49:07.341Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3d/b53c2d8949067f3f45491e250620e437569f1b4e6a028f2f5e721726283e/pyobjc_framework_Carbon-11.0-py3-none-any.whl", hash = "sha256:9a269042e8f5705897ac64d2b48515ba055462c88460cf140f5d8d4b8c806a42", size = 4768, upload_time = "2025-01-14T18:49:10.256Z" }, ] [[package]] @@ -2347,10 +2354,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532 } +sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532, upload_time = "2025-01-14T19:02:52.777Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/85/11047cfe2d31c242694d780783f0dea81d73cbb09929c7d4b918ce2d29bf/pyobjc_framework_CFNetwork-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e6905c86ccb5608f4153aacb931758ad39af8b708fcebb497431f9741f39e6d", size = 18988 }, - { url = "https://files.pythonhosted.org/packages/3e/6e/7d90c329030e7dd6ebbec217434820ff6158a3af9906e2abbb43e9b685d6/pyobjc_framework_CFNetwork-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f61010503073e3518e29d440079a7c0b40aef91be6d3c2032e492c21bada80b", size = 19144 }, + { url = "https://files.pythonhosted.org/packages/d9/85/11047cfe2d31c242694d780783f0dea81d73cbb09929c7d4b918ce2d29bf/pyobjc_framework_CFNetwork-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e6905c86ccb5608f4153aacb931758ad39af8b708fcebb497431f9741f39e6d", size = 18988, upload_time = "2025-01-14T18:48:54.869Z" }, + { url = "https://files.pythonhosted.org/packages/3e/6e/7d90c329030e7dd6ebbec217434820ff6158a3af9906e2abbb43e9b685d6/pyobjc_framework_CFNetwork-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f61010503073e3518e29d440079a7c0b40aef91be6d3c2032e492c21bada80b", size = 19144, upload_time = "2025-01-14T18:48:55.864Z" }, ] [[package]] @@ -2364,10 +2371,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740 } +sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740, upload_time = "2025-01-14T19:02:54.95Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/cf/a60e131bddf5cced32a3c0050d264f2255d63c45be398cede1db03ea8b51/pyobjc_framework_Cinematic-11.0-py2.py3-none-any.whl", hash = "sha256:281721969978d726ded9bae38c4acd6713495c399025ff2b4179fc02ec68b336", size = 4508 }, - { url = "https://files.pythonhosted.org/packages/09/a8/4ea347c1fc5774e2bbe7bb688fc625d583103d1e212f7b896ed19d14844b/pyobjc_framework_Cinematic-11.0-py3-none-any.whl", hash = "sha256:3a24f3528d7f77637f51fd1862cc8c79e4d0da4ba6fd3dd02b54adddec365826", size = 4580 }, + { url = "https://files.pythonhosted.org/packages/55/cf/a60e131bddf5cced32a3c0050d264f2255d63c45be398cede1db03ea8b51/pyobjc_framework_Cinematic-11.0-py2.py3-none-any.whl", hash = "sha256:281721969978d726ded9bae38c4acd6713495c399025ff2b4179fc02ec68b336", size = 4508, upload_time = "2025-01-14T18:49:11.202Z" }, + { url = "https://files.pythonhosted.org/packages/09/a8/4ea347c1fc5774e2bbe7bb688fc625d583103d1e212f7b896ed19d14844b/pyobjc_framework_Cinematic-11.0-py3-none-any.whl", hash = "sha256:3a24f3528d7f77637f51fd1862cc8c79e4d0da4ba6fd3dd02b54adddec365826", size = 4580, upload_time = "2025-01-14T18:49:12.251Z" }, ] [[package]] @@ -2378,10 +2385,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301, upload_time = "2025-01-14T19:02:55.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/77/2e31bcf1e9b63f6723c01329c1191ac163e79b0f548b7cd92414115c26ff/pyobjc_framework_ClassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:723a07591e1e40380c339b58033e8491e58be4080c0f77a26be0728f1c5025c8", size = 8776 }, - { url = "https://files.pythonhosted.org/packages/68/87/f566c4f1ffd1e383c7b38cd22753dfef0863f30bfdb0b3c5102293057fc2/pyobjc_framework_ClassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7c7ff2eb8a9d87cb69618668e96c41ed9467fd4b4a8fef517c49923c0f6418e6", size = 8794 }, + { url = "https://files.pythonhosted.org/packages/a8/77/2e31bcf1e9b63f6723c01329c1191ac163e79b0f548b7cd92414115c26ff/pyobjc_framework_ClassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:723a07591e1e40380c339b58033e8491e58be4080c0f77a26be0728f1c5025c8", size = 8776, upload_time = "2025-01-14T18:49:14.05Z" }, + { url = "https://files.pythonhosted.org/packages/68/87/f566c4f1ffd1e383c7b38cd22753dfef0863f30bfdb0b3c5102293057fc2/pyobjc_framework_ClassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7c7ff2eb8a9d87cb69618668e96c41ed9467fd4b4a8fef517c49923c0f6418e6", size = 8794, upload_time = "2025-01-14T18:49:14.985Z" }, ] [[package]] @@ -2395,10 +2402,10 @@ dependencies = [ { name = "pyobjc-framework-coredata" }, { name = "pyobjc-framework-corelocation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034 } +sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034, upload_time = "2025-01-14T19:02:56.769Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/db/9f914422be88eb2c917d67aebac9dde2e272ea1b510ca1e0db17a09db125/pyobjc_framework_CloudKit-11.0-py2.py3-none-any.whl", hash = "sha256:10cb153d7185dd260d21596f75fca8502236f6afd8e72e866cff8acd9c025f14", size = 10785 }, - { url = "https://files.pythonhosted.org/packages/53/73/239581763a1bd56475ebd9bdde52a79cf0b6cac20b3d4442283b1ef8705c/pyobjc_framework_CloudKit-11.0-py3-none-any.whl", hash = "sha256:b2376d92d5822ce7e4feefcffdc3f4d1d230929f1735793da6d36b52b161b552", size = 10854 }, + { url = "https://files.pythonhosted.org/packages/c2/db/9f914422be88eb2c917d67aebac9dde2e272ea1b510ca1e0db17a09db125/pyobjc_framework_CloudKit-11.0-py2.py3-none-any.whl", hash = "sha256:10cb153d7185dd260d21596f75fca8502236f6afd8e72e866cff8acd9c025f14", size = 10785, upload_time = "2025-01-14T18:49:21.369Z" }, + { url = "https://files.pythonhosted.org/packages/53/73/239581763a1bd56475ebd9bdde52a79cf0b6cac20b3d4442283b1ef8705c/pyobjc_framework_CloudKit-11.0-py3-none-any.whl", hash = "sha256:b2376d92d5822ce7e4feefcffdc3f4d1d230929f1735793da6d36b52b161b552", size = 10854, upload_time = "2025-01-14T18:49:23.612Z" }, ] [[package]] @@ -2408,10 +2415,10 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyobjc-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848 } +sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848, upload_time = "2025-01-14T19:03:00.125Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/97/81fd41ad90e9c241172110aa635a6239d56f50d75923aaedbbe351828580/pyobjc_framework_Cocoa-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3ea7be6e6dd801b297440de02d312ba3fa7fd3c322db747ae1cb237e975f5d33", size = 385534 }, - { url = "https://files.pythonhosted.org/packages/5b/8d/0e2558447c26b3ba64f7c9776a5a6c9d2ae8abf9d34308b174ae0934402e/pyobjc_framework_Cocoa-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:280a577b83c68175a28b2b7138d1d2d3111f2b2b66c30e86f81a19c2b02eae71", size = 385811 }, + { url = "https://files.pythonhosted.org/packages/23/97/81fd41ad90e9c241172110aa635a6239d56f50d75923aaedbbe351828580/pyobjc_framework_Cocoa-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3ea7be6e6dd801b297440de02d312ba3fa7fd3c322db747ae1cb237e975f5d33", size = 385534, upload_time = "2025-01-14T18:49:27.898Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8d/0e2558447c26b3ba64f7c9776a5a6c9d2ae8abf9d34308b174ae0934402e/pyobjc_framework_Cocoa-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:280a577b83c68175a28b2b7138d1d2d3111f2b2b66c30e86f81a19c2b02eae71", size = 385811, upload_time = "2025-01-14T18:49:29.259Z" }, ] [[package]] @@ -2422,10 +2429,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084 } +sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084, upload_time = "2025-01-14T19:03:01.98Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ee/95883b6fbdbeecd99217c50c415ca024db5beb1923b935189a113412203d/pyobjc_framework_Collaboration-11.0-py2.py3-none-any.whl", hash = "sha256:acf11e584e21f6342e6d7be1675f36c92804082c29d2f373d1ca623a63959e76", size = 4807 }, - { url = "https://files.pythonhosted.org/packages/c0/e5/d3ba7e3e3f306ba93c021c083287c668704d84605e0f788583abcfde815f/pyobjc_framework_Collaboration-11.0-py3-none-any.whl", hash = "sha256:e7789503ea9280ba365ce2c4e6c7c8b13dfa9174b2ecf9d174bbf9773f25f97a", size = 4876 }, + { url = "https://files.pythonhosted.org/packages/c1/ee/95883b6fbdbeecd99217c50c415ca024db5beb1923b935189a113412203d/pyobjc_framework_Collaboration-11.0-py2.py3-none-any.whl", hash = "sha256:acf11e584e21f6342e6d7be1675f36c92804082c29d2f373d1ca623a63959e76", size = 4807, upload_time = "2025-01-14T18:49:37.145Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e5/d3ba7e3e3f306ba93c021c083287c668704d84605e0f788583abcfde815f/pyobjc_framework_Collaboration-11.0-py3-none-any.whl", hash = "sha256:e7789503ea9280ba365ce2c4e6c7c8b13dfa9174b2ecf9d174bbf9773f25f97a", size = 4876, upload_time = "2025-01-14T18:49:39.887Z" }, ] [[package]] @@ -2436,10 +2443,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249 } +sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249, upload_time = "2025-01-14T19:03:02.887Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/16/d806b5c3ff5bf8f46a4770f89b2076d2596c1301c851c60bb43aea457cd3/pyobjc_framework_ColorSync-11.0-py2.py3-none-any.whl", hash = "sha256:24f5c3e0987bfdfe6a0de36f2f908e30ea52000eb649db7b0373928140518163", size = 5916 }, - { url = "https://files.pythonhosted.org/packages/06/18/777bad37aab42f75d2ef2efb9240308c15c33b3a0636278111ec6c5df550/pyobjc_framework_ColorSync-11.0-py3-none-any.whl", hash = "sha256:cbee2211f64be927eb4e4717bf6e275bf28954ed86e4a4655d367c30f856494d", size = 5987 }, + { url = "https://files.pythonhosted.org/packages/78/16/d806b5c3ff5bf8f46a4770f89b2076d2596c1301c851c60bb43aea457cd3/pyobjc_framework_ColorSync-11.0-py2.py3-none-any.whl", hash = "sha256:24f5c3e0987bfdfe6a0de36f2f908e30ea52000eb649db7b0373928140518163", size = 5916, upload_time = "2025-01-14T18:49:41.273Z" }, + { url = "https://files.pythonhosted.org/packages/06/18/777bad37aab42f75d2ef2efb9240308c15c33b3a0636278111ec6c5df550/pyobjc_framework_ColorSync-11.0-py3-none-any.whl", hash = "sha256:cbee2211f64be927eb4e4717bf6e275bf28954ed86e4a4655d367c30f856494d", size = 5987, upload_time = "2025-01-14T18:49:42.286Z" }, ] [[package]] @@ -2450,10 +2457,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253 } +sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253, upload_time = "2025-01-14T19:03:03.743Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/4f/b7a7b08535015494940a62fd63825eccf4cace7f8ca87050f0837470eca8/pyobjc_framework_Contacts-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b16758fc1edc40f0ec288d67b7e39b59609fb1df2523f4362c958d150619dbe5", size = 11880 }, - { url = "https://files.pythonhosted.org/packages/07/4b/0d2b41a32b6432182548cb84bb6b1c3228a7ff428ea15dfaf812b39c028f/pyobjc_framework_Contacts-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:80972851e2163b94d82fd4b0d9801790ad420dffa91a37c90fa2949031881c02", size = 11957 }, + { url = "https://files.pythonhosted.org/packages/91/4f/b7a7b08535015494940a62fd63825eccf4cace7f8ca87050f0837470eca8/pyobjc_framework_Contacts-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b16758fc1edc40f0ec288d67b7e39b59609fb1df2523f4362c958d150619dbe5", size = 11880, upload_time = "2025-01-14T18:49:44.316Z" }, + { url = "https://files.pythonhosted.org/packages/07/4b/0d2b41a32b6432182548cb84bb6b1c3228a7ff428ea15dfaf812b39c028f/pyobjc_framework_Contacts-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:80972851e2163b94d82fd4b0d9801790ad420dffa91a37c90fa2949031881c02", size = 11957, upload_time = "2025-01-14T18:49:46.952Z" }, ] [[package]] @@ -2465,10 +2472,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-contacts" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486, upload_time = "2025-01-14T19:03:04.72Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/47/b1dbe48c64e2d061bf8b4ee532413b97e6c5748fdba43598a30421086fcc/pyobjc_framework_ContactsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd3efaf3f67e92704f41927c5de06ccc4aa9daa09865cba7ac476da9c6e1c3c2", size = 7734 }, - { url = "https://files.pythonhosted.org/packages/5d/c5/465656c744301bfb7640e4077c57170d245843311e0e66702b53295e2534/pyobjc_framework_ContactsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9c85dccdf518a0ac80c627daca32d56a4636e3f118359579de51a428e85ba7", size = 7739 }, + { url = "https://files.pythonhosted.org/packages/fc/47/b1dbe48c64e2d061bf8b4ee532413b97e6c5748fdba43598a30421086fcc/pyobjc_framework_ContactsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd3efaf3f67e92704f41927c5de06ccc4aa9daa09865cba7ac476da9c6e1c3c2", size = 7734, upload_time = "2025-01-14T18:49:52.765Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c5/465656c744301bfb7640e4077c57170d245843311e0e66702b53295e2534/pyobjc_framework_ContactsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9c85dccdf518a0ac80c627daca32d56a4636e3f118359579de51a428e85ba7", size = 7739, upload_time = "2025-01-14T18:49:55.507Z" }, ] [[package]] @@ -2479,10 +2486,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507 } +sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507, upload_time = "2025-01-14T19:03:05.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/f8/6f583376d2ef6a6123141d310f7f7e3e93ba9129ffbbc6eb26e25c4289c5/pyobjc_framework_CoreAudio-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:143cd44d5c069aee1abc5a88794e9531250b9fe70a98f6a08e493184dcf64b3e", size = 35750 }, - { url = "https://files.pythonhosted.org/packages/df/14/b33556c06529a3c54853c41c5163e30a3fb9b2ae920e0c65a42ccd82e279/pyobjc_framework_CoreAudio-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d26eac5bc325bf046fc0bfdaa3322ddc828690dab726275f1c4c118bb888cc00", size = 36584 }, + { url = "https://files.pythonhosted.org/packages/78/f8/6f583376d2ef6a6123141d310f7f7e3e93ba9129ffbbc6eb26e25c4289c5/pyobjc_framework_CoreAudio-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:143cd44d5c069aee1abc5a88794e9531250b9fe70a98f6a08e493184dcf64b3e", size = 35750, upload_time = "2025-01-14T18:50:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/df/14/b33556c06529a3c54853c41c5163e30a3fb9b2ae920e0c65a42ccd82e279/pyobjc_framework_CoreAudio-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d26eac5bc325bf046fc0bfdaa3322ddc828690dab726275f1c4c118bb888cc00", size = 36584, upload_time = "2025-01-14T18:50:01.806Z" }, ] [[package]] @@ -2494,10 +2501,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coreaudio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450 } +sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450, upload_time = "2025-01-14T19:03:06.681Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/b9/d75a4da2d6a3cb75bafd363c447d45e134fe78a340dee408423a40c04aac/pyobjc_framework_CoreAudioKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dbf01f2625689b392c2ba02f3ab8186c914d84d6bd896bdef5181a15a9463df", size = 7192 }, - { url = "https://files.pythonhosted.org/packages/46/1f/5c15023665cc0476cdd7cbc054d5b06489fc09990f068768ed2fda8a02a2/pyobjc_framework_CoreAudioKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ccf2d92052a446d1d38bfd7eaa1dcd2451d59c37e73070a9a1fee394a532d9d", size = 7214 }, + { url = "https://files.pythonhosted.org/packages/4c/b9/d75a4da2d6a3cb75bafd363c447d45e134fe78a340dee408423a40c04aac/pyobjc_framework_CoreAudioKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dbf01f2625689b392c2ba02f3ab8186c914d84d6bd896bdef5181a15a9463df", size = 7192, upload_time = "2025-01-14T18:50:09.524Z" }, + { url = "https://files.pythonhosted.org/packages/46/1f/5c15023665cc0476cdd7cbc054d5b06489fc09990f068768ed2fda8a02a2/pyobjc_framework_CoreAudioKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ccf2d92052a446d1d38bfd7eaa1dcd2451d59c37e73070a9a1fee394a532d9d", size = 7214, upload_time = "2025-01-14T18:50:10.399Z" }, ] [[package]] @@ -2508,10 +2515,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797 } +sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797, upload_time = "2025-01-14T19:03:07.584Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/a8/df866e8a84fd33d29af1ee383f13715bbd98ad67d5795dfb276a3887560f/pyobjc_framework_CoreBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:044069d63447554ba2c65cb1bf58d489d14ea279344810386392e583a2e611ef", size = 13683 }, - { url = "https://files.pythonhosted.org/packages/44/fa/ad2165bc93c9d3fb174a0d8d5a4db3a7dfcf4dcaeca7913d59748ef62fdb/pyobjc_framework_CoreBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bae8f909512d014eed85f80deae671185af4bb5a671fba19f85c7b4c973b61bb", size = 13713 }, + { url = "https://files.pythonhosted.org/packages/53/a8/df866e8a84fd33d29af1ee383f13715bbd98ad67d5795dfb276a3887560f/pyobjc_framework_CoreBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:044069d63447554ba2c65cb1bf58d489d14ea279344810386392e583a2e611ef", size = 13683, upload_time = "2025-01-14T18:50:16.231Z" }, + { url = "https://files.pythonhosted.org/packages/44/fa/ad2165bc93c9d3fb174a0d8d5a4db3a7dfcf4dcaeca7913d59748ef62fdb/pyobjc_framework_CoreBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bae8f909512d014eed85f80deae671185af4bb5a671fba19f85c7b4c973b61bb", size = 13713, upload_time = "2025-01-14T18:50:17.122Z" }, ] [[package]] @@ -2522,10 +2529,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029 } +sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029, upload_time = "2025-01-14T19:03:08.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/b0/32c23ee168e5081391daa8737fddde79670b083e948dffb8d74308f1dd43/pyobjc_framework_CoreData-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74ac5e7658df10544708f6017a8823a100fbe41dc3aa9f61bf2fd4f8773c3dd7", size = 16194 }, - { url = "https://files.pythonhosted.org/packages/6a/9e/39ca8124c6d87dc6fa85bcf850a2c23a062a408a26300062041c10363a3f/pyobjc_framework_CoreData-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c23b8c9106b0ec6f43aca80d2b2e0b0cc8fcb4ba78db4ae3c1f39a67464357d7", size = 16208 }, + { url = "https://files.pythonhosted.org/packages/86/b0/32c23ee168e5081391daa8737fddde79670b083e948dffb8d74308f1dd43/pyobjc_framework_CoreData-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74ac5e7658df10544708f6017a8823a100fbe41dc3aa9f61bf2fd4f8773c3dd7", size = 16194, upload_time = "2025-01-14T18:50:22.924Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/39ca8124c6d87dc6fa85bcf850a2c23a062a408a26300062041c10363a3f/pyobjc_framework_CoreData-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c23b8c9106b0ec6f43aca80d2b2e0b0cc8fcb4ba78db4ae3c1f39a67464357d7", size = 16208, upload_time = "2025-01-14T18:50:23.838Z" }, ] [[package]] @@ -2536,10 +2543,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956, upload_time = "2025-01-14T19:03:09.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/16/16d4365c8da1f708e145500237a3cdbbdde3e83b7f3f8673b038efac03b9/pyobjc_framework_CoreHaptics-11.0-py2.py3-none-any.whl", hash = "sha256:ff1d8f58dd3b29287dfad16a60bb45706c91f1910e400b632cb664eb2e56588b", size = 5307 }, - { url = "https://files.pythonhosted.org/packages/12/72/b9fca92b3704af8f5f3b5507d0d9f3d0f5eb16605664de669f4468858627/pyobjc_framework_CoreHaptics-11.0-py3-none-any.whl", hash = "sha256:33f7a767efe6867fa6821ad73872ea88aec44650a22217bcdc9c1ec7c41fd9dc", size = 5377 }, + { url = "https://files.pythonhosted.org/packages/96/16/16d4365c8da1f708e145500237a3cdbbdde3e83b7f3f8673b038efac03b9/pyobjc_framework_CoreHaptics-11.0-py2.py3-none-any.whl", hash = "sha256:ff1d8f58dd3b29287dfad16a60bb45706c91f1910e400b632cb664eb2e56588b", size = 5307, upload_time = "2025-01-14T18:50:33.074Z" }, + { url = "https://files.pythonhosted.org/packages/12/72/b9fca92b3704af8f5f3b5507d0d9f3d0f5eb16605664de669f4468858627/pyobjc_framework_CoreHaptics-11.0-py3-none-any.whl", hash = "sha256:33f7a767efe6867fa6821ad73872ea88aec44650a22217bcdc9c1ec7c41fd9dc", size = 5377, upload_time = "2025-01-14T18:50:34.484Z" }, ] [[package]] @@ -2550,10 +2557,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955, upload_time = "2025-01-14T19:03:10.707Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/99/c7844f6e583f4764c6fab4a5b5ad9e949c6fce8c30f95226118bead41e01/pyobjc_framework_CoreLocation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:046f211a23de55364c8553cfd660dc5adeff28af4d25f5ed9b5a8bfa83266b4d", size = 13075 }, - { url = "https://files.pythonhosted.org/packages/88/6b/bb4fbcd259404fb60fdbfecef3c426dc23da5a0f0bc5bf96a4169b047478/pyobjc_framework_CoreLocation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9bca9974f143bc9e93bd7ec4ef91655964d8ad0ca5ffccc8404fb6f098fa08dc", size = 13076 }, + { url = "https://files.pythonhosted.org/packages/10/99/c7844f6e583f4764c6fab4a5b5ad9e949c6fce8c30f95226118bead41e01/pyobjc_framework_CoreLocation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:046f211a23de55364c8553cfd660dc5adeff28af4d25f5ed9b5a8bfa83266b4d", size = 13075, upload_time = "2025-01-14T18:50:37.789Z" }, + { url = "https://files.pythonhosted.org/packages/88/6b/bb4fbcd259404fb60fdbfecef3c426dc23da5a0f0bc5bf96a4169b047478/pyobjc_framework_CoreLocation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9bca9974f143bc9e93bd7ec4ef91655964d8ad0ca5ffccc8404fb6f098fa08dc", size = 13076, upload_time = "2025-01-14T18:50:38.693Z" }, ] [[package]] @@ -2564,10 +2571,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161 } +sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161, upload_time = "2025-01-14T19:03:12.291Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/b3/7baca352ddd7256840a4eb8f38fda39bc2e023b222b86d11c1a77cc0a8fa/pyobjc_framework_CoreMedia-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:057e63e533577fe5d764a5a9d307f60e8d9c58803112951ace42183abe9437e3", size = 29422 }, - { url = "https://files.pythonhosted.org/packages/68/73/7ed3eba9c5a4a2071c3a64d6b1388d13474ad8d972529f3d5c950942513d/pyobjc_framework_CoreMedia-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:afd8eb59f5ce0730ff15476ad3989aa84ffb8d8d02c9b8b2c9c1248b0541dbff", size = 29297 }, + { url = "https://files.pythonhosted.org/packages/a5/b3/7baca352ddd7256840a4eb8f38fda39bc2e023b222b86d11c1a77cc0a8fa/pyobjc_framework_CoreMedia-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:057e63e533577fe5d764a5a9d307f60e8d9c58803112951ace42183abe9437e3", size = 29422, upload_time = "2025-01-14T18:50:56.964Z" }, + { url = "https://files.pythonhosted.org/packages/68/73/7ed3eba9c5a4a2071c3a64d6b1388d13474ad8d972529f3d5c950942513d/pyobjc_framework_CoreMedia-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:afd8eb59f5ce0730ff15476ad3989aa84ffb8d8d02c9b8b2c9c1248b0541dbff", size = 29297, upload_time = "2025-01-14T18:50:58.388Z" }, ] [[package]] @@ -2578,10 +2585,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856 } +sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856, upload_time = "2025-01-14T19:03:14.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/02/09fda96c4727ff0c632c3cf4b09faa5b82be9f18422860dd80b5bc676ae1/pyobjc_framework_CoreMediaIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4182b91c72923d5c4d52eca3c221cc6f149d80a96242c0caab1d5bc9ccbcbbb", size = 17492 }, - { url = "https://files.pythonhosted.org/packages/3f/db/a7b11cbf7d31964a65c5593ac30a02b0db35260845431046d467b08fc059/pyobjc_framework_CoreMediaIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ad1e0f74126b6c6d25017e4ba08f66fe5422c902060d64b69e06a0c10214355", size = 17534 }, + { url = "https://files.pythonhosted.org/packages/27/02/09fda96c4727ff0c632c3cf4b09faa5b82be9f18422860dd80b5bc676ae1/pyobjc_framework_CoreMediaIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4182b91c72923d5c4d52eca3c221cc6f149d80a96242c0caab1d5bc9ccbcbbb", size = 17492, upload_time = "2025-01-14T18:51:04.559Z" }, + { url = "https://files.pythonhosted.org/packages/3f/db/a7b11cbf7d31964a65c5593ac30a02b0db35260845431046d467b08fc059/pyobjc_framework_CoreMediaIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ad1e0f74126b6c6d25017e4ba08f66fe5422c902060d64b69e06a0c10214355", size = 17534, upload_time = "2025-01-14T18:51:06.752Z" }, ] [[package]] @@ -2592,10 +2599,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342 } +sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342, upload_time = "2025-01-14T19:03:15.235Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/b9/c67886891ad3cd21107cf1e65f1431cbdcff33acd74bf55ad3d6e10f3adf/pyobjc_framework_CoreMIDI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:78dec1bcd253a0385ac0b758a455e2a9367fc3cb9e2306d410c61bafa8d4c2eb", size = 24314 }, - { url = "https://files.pythonhosted.org/packages/c0/7a/0639bc1ac35373b68f0f15fbcb9bb4f317cc4452997ea8e611ce79f623e9/pyobjc_framework_CoreMIDI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:97158830d76b999255d87191f31624d4373ee8ff662af4f4376c584cfb805573", size = 24346 }, + { url = "https://files.pythonhosted.org/packages/4d/b9/c67886891ad3cd21107cf1e65f1431cbdcff33acd74bf55ad3d6e10f3adf/pyobjc_framework_CoreMIDI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:78dec1bcd253a0385ac0b758a455e2a9367fc3cb9e2306d410c61bafa8d4c2eb", size = 24314, upload_time = "2025-01-14T18:50:44.817Z" }, + { url = "https://files.pythonhosted.org/packages/c0/7a/0639bc1ac35373b68f0f15fbcb9bb4f317cc4452997ea8e611ce79f623e9/pyobjc_framework_CoreMIDI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:97158830d76b999255d87191f31624d4373ee8ff662af4f4376c584cfb805573", size = 24346, upload_time = "2025-01-14T18:50:45.805Z" }, ] [[package]] @@ -2606,10 +2613,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452, upload_time = "2025-01-14T19:03:16.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/dc/334823bb3faa490259df7611772e804eb883c56436fc69123e8a3a5ba0ea/pyobjc_framework_CoreML-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e290ad9c0ac5f057ce3885d35e33fadc115f59111f2e04f168c45e2890cb86e8", size = 11320 }, - { url = "https://files.pythonhosted.org/packages/90/9f/3d053b95fbeeaf480d33fcc067504e205049591f6bee17e3a700b988d96c/pyobjc_framework_CoreML-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:48320a57589634c206d659799284a5133aaa006cf4562f772697df5b479043e4", size = 11321 }, + { url = "https://files.pythonhosted.org/packages/50/dc/334823bb3faa490259df7611772e804eb883c56436fc69123e8a3a5ba0ea/pyobjc_framework_CoreML-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e290ad9c0ac5f057ce3885d35e33fadc115f59111f2e04f168c45e2890cb86e8", size = 11320, upload_time = "2025-01-14T18:50:50.914Z" }, + { url = "https://files.pythonhosted.org/packages/90/9f/3d053b95fbeeaf480d33fcc067504e205049591f6bee17e3a700b988d96c/pyobjc_framework_CoreML-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:48320a57589634c206d659799284a5133aaa006cf4562f772697df5b479043e4", size = 11321, upload_time = "2025-01-14T18:50:51.803Z" }, ] [[package]] @@ -2620,10 +2627,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508 } +sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508, upload_time = "2025-01-14T19:03:17.254Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/35/da29fd7350cd68bfe70f30a9e03e1350d7363c7c4fcdb5b0cd16f4bb47e2/pyobjc_framework_CoreMotion-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8e32de44e30028500e4d17c114eea69e7e74e5ae7aef6c208edc5bac34dfc21e", size = 10229 }, - { url = "https://files.pythonhosted.org/packages/ca/f6/8061b58f0f3e1daf34c19511f0eeefe4ad66d10d1994b84d7fa3733b7852/pyobjc_framework_CoreMotion-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:697a3121615e95e56808f388b0882217a50e5ff6b459eccae93f2809d5ea4389", size = 10250 }, + { url = "https://files.pythonhosted.org/packages/01/35/da29fd7350cd68bfe70f30a9e03e1350d7363c7c4fcdb5b0cd16f4bb47e2/pyobjc_framework_CoreMotion-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8e32de44e30028500e4d17c114eea69e7e74e5ae7aef6c208edc5bac34dfc21e", size = 10229, upload_time = "2025-01-14T18:51:12.825Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f6/8061b58f0f3e1daf34c19511f0eeefe4ad66d10d1994b84d7fa3733b7852/pyobjc_framework_CoreMotion-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:697a3121615e95e56808f388b0882217a50e5ff6b459eccae93f2809d5ea4389", size = 10250, upload_time = "2025-01-14T18:51:13.746Z" }, ] [[package]] @@ -2635,10 +2642,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-fsevents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406, upload_time = "2025-01-14T19:03:19.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/cc/3899a59ed62fa36d2c1b95b94ff52e181ac48fde4011b68ca6abcbddd47a/pyobjc_framework_CoreServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7538ca6e22f4da0c3a70ddd9781f9240a3fe2fd7a7aa4dfb31c31f2532f008e", size = 30258 }, - { url = "https://files.pythonhosted.org/packages/82/7b/8e059764951d0414f053bfebb6b1fba803a3b14397755cfd388b0a6363a7/pyobjc_framework_CoreServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b175b5aa7a78484fd07b93533174b125901a6b791df2c51e05df1ea5d5badab", size = 30250 }, + { url = "https://files.pythonhosted.org/packages/e9/cc/3899a59ed62fa36d2c1b95b94ff52e181ac48fde4011b68ca6abcbddd47a/pyobjc_framework_CoreServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7538ca6e22f4da0c3a70ddd9781f9240a3fe2fd7a7aa4dfb31c31f2532f008e", size = 30258, upload_time = "2025-01-14T18:51:20.487Z" }, + { url = "https://files.pythonhosted.org/packages/82/7b/8e059764951d0414f053bfebb6b1fba803a3b14397755cfd388b0a6363a7/pyobjc_framework_CoreServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b175b5aa7a78484fd07b93533174b125901a6b791df2c51e05df1ea5d5badab", size = 30250, upload_time = "2025-01-14T18:51:21.448Z" }, ] [[package]] @@ -2649,10 +2656,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130 } +sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130, upload_time = "2025-01-14T19:03:20.457Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/f1/54f9522d7f6ec7a6618c86abe0236869f61dd371b49df02dff7930433656/pyobjc_framework_CoreSpotlight-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:551878bfb9cc815fe2532fdf455f500dda44f8cd203dd836a6f1eb5cc0d49a9a", size = 9579 }, - { url = "https://files.pythonhosted.org/packages/6c/24/dae8d0be7cb90328a8c1100c454e52faef95acc59940796f530b665b9555/pyobjc_framework_CoreSpotlight-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b0c595d0a422a0f81008df93a0a2b38a1fd62434c6f61e31f1dceec927803b80", size = 9597 }, + { url = "https://files.pythonhosted.org/packages/ba/f1/54f9522d7f6ec7a6618c86abe0236869f61dd371b49df02dff7930433656/pyobjc_framework_CoreSpotlight-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:551878bfb9cc815fe2532fdf455f500dda44f8cd203dd836a6f1eb5cc0d49a9a", size = 9579, upload_time = "2025-01-14T18:51:28.889Z" }, + { url = "https://files.pythonhosted.org/packages/6c/24/dae8d0be7cb90328a8c1100c454e52faef95acc59940796f530b665b9555/pyobjc_framework_CoreSpotlight-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b0c595d0a422a0f81008df93a0a2b38a1fd62434c6f61e31f1dceec927803b80", size = 9597, upload_time = "2025-01-14T18:51:30.677Z" }, ] [[package]] @@ -2664,10 +2671,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222 } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222, upload_time = "2025-01-14T19:03:21.521Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/20/b8a967101b585a2425ffe645135f8618edd51e1430aeb668373475a07d1f/pyobjc_framework_CoreText-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56a4889858308b0d9f147d568b4d91c441cc0ffd332497cb4f709bb1990450c1", size = 30397 }, - { url = "https://files.pythonhosted.org/packages/0d/14/d300b8bf18acd1d98d40820d2a9b5c5b6cf96325bdfc5020bc963218e001/pyobjc_framework_CoreText-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb90e7f370b3fd7cb2fb442e3dc63fedf0b4af6908db1c18df694d10dc94669d", size = 30456 }, + { url = "https://files.pythonhosted.org/packages/f6/20/b8a967101b585a2425ffe645135f8618edd51e1430aeb668373475a07d1f/pyobjc_framework_CoreText-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56a4889858308b0d9f147d568b4d91c441cc0ffd332497cb4f709bb1990450c1", size = 30397, upload_time = "2025-01-14T18:51:35.844Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/d300b8bf18acd1d98d40820d2a9b5c5b6cf96325bdfc5020bc963218e001/pyobjc_framework_CoreText-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb90e7f370b3fd7cb2fb442e3dc63fedf0b4af6908db1c18df694d10dc94669d", size = 30456, upload_time = "2025-01-14T18:51:36.962Z" }, ] [[package]] @@ -2678,10 +2685,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254 } +sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254, upload_time = "2025-01-14T19:03:23.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/e7/a869bf3e8673c8fdf496706672dac77fc305493db3c1057e3ca5f8d49c3f/pyobjc_framework_CoreWLAN-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4384ba68d4beb4d610ca0d661593e16efe541faf1790222b898b3f4dd389c98a", size = 9895 }, - { url = "https://files.pythonhosted.org/packages/7c/d7/87626e23f010aa865eef10c796d1d87ddd87b78656f4e4ef0e808c8268f7/pyobjc_framework_CoreWLAN-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f5c365f6ebdae4a87d534cf8af877a57d2aabe50fe5949a9334e75173291898", size = 9917 }, + { url = "https://files.pythonhosted.org/packages/da/e7/a869bf3e8673c8fdf496706672dac77fc305493db3c1057e3ca5f8d49c3f/pyobjc_framework_CoreWLAN-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4384ba68d4beb4d610ca0d661593e16efe541faf1790222b898b3f4dd389c98a", size = 9895, upload_time = "2025-01-14T18:51:43.245Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d7/87626e23f010aa865eef10c796d1d87ddd87b78656f4e4ef0e808c8268f7/pyobjc_framework_CoreWLAN-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f5c365f6ebdae4a87d534cf8af877a57d2aabe50fe5949a9334e75173291898", size = 9917, upload_time = "2025-01-14T18:51:45.362Z" }, ] [[package]] @@ -2692,10 +2699,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734 } +sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734, upload_time = "2025-01-14T19:03:24.851Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/60/ddf022ce94f829a605992f11b9bfa861d7a1579f794e03d969c209d0de2a/pyobjc_framework_CryptoTokenKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c42620047cc75a749fbed045d181dc76284bc27edea904b97df1ad82c2fdafc", size = 12949 }, - { url = "https://files.pythonhosted.org/packages/d7/2d/9641cae1800281faace48698646f71c3de23ea1343031c12f6637d31e6f1/pyobjc_framework_CryptoTokenKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:95b05efb06b09987e23fb62dc3af378f38cfd0bd5872940cd95cf0f39dac6a57", size = 12978 }, + { url = "https://files.pythonhosted.org/packages/ac/60/ddf022ce94f829a605992f11b9bfa861d7a1579f794e03d969c209d0de2a/pyobjc_framework_CryptoTokenKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c42620047cc75a749fbed045d181dc76284bc27edea904b97df1ad82c2fdafc", size = 12949, upload_time = "2025-01-14T18:51:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2d/9641cae1800281faace48698646f71c3de23ea1343031c12f6637d31e6f1/pyobjc_framework_CryptoTokenKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:95b05efb06b09987e23fb62dc3af378f38cfd0bd5872940cd95cf0f39dac6a57", size = 12978, upload_time = "2025-01-14T18:51:51.215Z" }, ] [[package]] @@ -2706,10 +2713,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738 } +sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738, upload_time = "2025-01-14T19:03:27.054Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/a1/63653827a78c8329a0106ac06e68ec0434e7f104f022dee5929bdf8fed62/pyobjc_framework_DataDetection-11.0-py2.py3-none-any.whl", hash = "sha256:0fd191ddee9bc6a491e05dfb7de780c0266fd6c90ca783e168786c4b0b5d7d7c", size = 3428 }, - { url = "https://files.pythonhosted.org/packages/1b/61/ee4579efb7c02b794d26ab0458722598726678d0bb227c9aa925a34f36af/pyobjc_framework_DataDetection-11.0-py3-none-any.whl", hash = "sha256:21b4a1dbf6cb56fdc971224476453dd1a7a4bb72d2c670444e81ae96fde97cb2", size = 3501 }, + { url = "https://files.pythonhosted.org/packages/11/a1/63653827a78c8329a0106ac06e68ec0434e7f104f022dee5929bdf8fed62/pyobjc_framework_DataDetection-11.0-py2.py3-none-any.whl", hash = "sha256:0fd191ddee9bc6a491e05dfb7de780c0266fd6c90ca783e168786c4b0b5d7d7c", size = 3428, upload_time = "2025-01-14T18:51:58.111Z" }, + { url = "https://files.pythonhosted.org/packages/1b/61/ee4579efb7c02b794d26ab0458722598726678d0bb227c9aa925a34f36af/pyobjc_framework_DataDetection-11.0-py3-none-any.whl", hash = "sha256:21b4a1dbf6cb56fdc971224476453dd1a7a4bb72d2c670444e81ae96fde97cb2", size = 3501, upload_time = "2025-01-14T18:51:59.104Z" }, ] [[package]] @@ -2720,10 +2727,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198 } +sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198, upload_time = "2025-01-14T19:03:27.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/c1/d889e1c515c23b911594aa0b53a9d8ab6173e07adaaad8db89324a731fb7/pyobjc_framework_DeviceCheck-11.0-py2.py3-none-any.whl", hash = "sha256:d9252173a57dfba09ae37ccc3049f4b4990c1cbdcde338622b42c66296a8740e", size = 3612 }, - { url = "https://files.pythonhosted.org/packages/65/8b/fa0cc2da2d49897f64e27a8a4e2a68f5784515f1adcea3a90f90b8ae8d44/pyobjc_framework_DeviceCheck-11.0-py3-none-any.whl", hash = "sha256:e8ed3965808963b2f0a7e069537d752bc659b75db1901cc24e5138925b9a7052", size = 3684 }, + { url = "https://files.pythonhosted.org/packages/5c/c1/d889e1c515c23b911594aa0b53a9d8ab6173e07adaaad8db89324a731fb7/pyobjc_framework_DeviceCheck-11.0-py2.py3-none-any.whl", hash = "sha256:d9252173a57dfba09ae37ccc3049f4b4990c1cbdcde338622b42c66296a8740e", size = 3612, upload_time = "2025-01-14T18:52:00.097Z" }, + { url = "https://files.pythonhosted.org/packages/65/8b/fa0cc2da2d49897f64e27a8a4e2a68f5784515f1adcea3a90f90b8ae8d44/pyobjc_framework_DeviceCheck-11.0-py3-none-any.whl", hash = "sha256:e8ed3965808963b2f0a7e069537d752bc659b75db1901cc24e5138925b9a7052", size = 3684, upload_time = "2025-01-14T18:52:02.389Z" }, ] [[package]] @@ -2734,10 +2741,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511, upload_time = "2025-01-14T19:03:32.132Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/be/3353a87691796a277ff4c048c4fa9a43db6f353fd683e8bb9e297651950c/pyobjc_framework_DeviceDiscoveryExtension-11.0-py2.py3-none-any.whl", hash = "sha256:82032e567d0031839d626947368d6d3d4ca97c915f15d2779a444cf4b2ffa4a3", size = 4194 }, - { url = "https://files.pythonhosted.org/packages/06/87/52137a60498c03ab0acd3b9eadafe3c371c12e0549718e6a1f0fff8b7725/pyobjc_framework_DeviceDiscoveryExtension-11.0-py3-none-any.whl", hash = "sha256:9c94057173f13472089d561b780d93b5aa244d048b4760a0e1ab54fe7c2253c5", size = 4265 }, + { url = "https://files.pythonhosted.org/packages/2a/be/3353a87691796a277ff4c048c4fa9a43db6f353fd683e8bb9e297651950c/pyobjc_framework_DeviceDiscoveryExtension-11.0-py2.py3-none-any.whl", hash = "sha256:82032e567d0031839d626947368d6d3d4ca97c915f15d2779a444cf4b2ffa4a3", size = 4194, upload_time = "2025-01-14T18:52:03.253Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/52137a60498c03ab0acd3b9eadafe3c371c12e0549718e6a1f0fff8b7725/pyobjc_framework_DeviceDiscoveryExtension-11.0-py3-none-any.whl", hash = "sha256:9c94057173f13472089d561b780d93b5aa244d048b4760a0e1ab54fe7c2253c5", size = 4265, upload_time = "2025-01-14T18:52:05.101Z" }, ] [[package]] @@ -2748,10 +2755,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-coreservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823, upload_time = "2025-01-14T19:03:32.942Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/68/5ea9766a8a6301f1a2ee39d595fe03d50b84b979d3d059e3e0ff541eab45/pyobjc_framework_DictionaryServices-11.0-py2.py3-none-any.whl", hash = "sha256:7c081371855240ac8e22783a71f32393c0f1e0b94d2fd193e8fef0a8be007080", size = 3829 }, - { url = "https://files.pythonhosted.org/packages/dd/c4/62b73f813c012f72a3a8e2f6326506803b45e91dc4ce6683e02a52a7f414/pyobjc_framework_DictionaryServices-11.0-py3-none-any.whl", hash = "sha256:15cdc3b64cb73713ee928cdcc0a12c845729f117bb8e73c7511f6e3f256d9d39", size = 3901 }, + { url = "https://files.pythonhosted.org/packages/0a/68/5ea9766a8a6301f1a2ee39d595fe03d50b84b979d3d059e3e0ff541eab45/pyobjc_framework_DictionaryServices-11.0-py2.py3-none-any.whl", hash = "sha256:7c081371855240ac8e22783a71f32393c0f1e0b94d2fd193e8fef0a8be007080", size = 3829, upload_time = "2025-01-14T18:52:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c4/62b73f813c012f72a3a8e2f6326506803b45e91dc4ce6683e02a52a7f414/pyobjc_framework_DictionaryServices-11.0-py3-none-any.whl", hash = "sha256:15cdc3b64cb73713ee928cdcc0a12c845729f117bb8e73c7511f6e3f256d9d39", size = 3901, upload_time = "2025-01-14T18:52:08.403Z" }, ] [[package]] @@ -2762,10 +2769,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426 } +sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426, upload_time = "2025-01-14T19:03:35.589Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/0b/fbe460ccddb4c613eb04e2b81cc9c75b0e0c407fd9fb91776381416f99af/pyobjc_framework_DiscRecording-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eab79d83c2d974aa5564f3f6f4415218573dca69010026d2d000d232494a9d81", size = 14491 }, - { url = "https://files.pythonhosted.org/packages/10/6f/c4c220d979771f4d7782deddef5ea9026baa177abe81cbe63d626a215de7/pyobjc_framework_DiscRecording-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e309e7394aed23d6ccce2e035f23c0c015d029c2ad531c6b1dce820b7eea8512", size = 14505 }, + { url = "https://files.pythonhosted.org/packages/7e/0b/fbe460ccddb4c613eb04e2b81cc9c75b0e0c407fd9fb91776381416f99af/pyobjc_framework_DiscRecording-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eab79d83c2d974aa5564f3f6f4415218573dca69010026d2d000d232494a9d81", size = 14491, upload_time = "2025-01-14T18:52:10.415Z" }, + { url = "https://files.pythonhosted.org/packages/10/6f/c4c220d979771f4d7782deddef5ea9026baa177abe81cbe63d626a215de7/pyobjc_framework_DiscRecording-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e309e7394aed23d6ccce2e035f23c0c015d029c2ad531c6b1dce820b7eea8512", size = 14505, upload_time = "2025-01-14T18:52:11.414Z" }, ] [[package]] @@ -2777,10 +2784,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-discrecording" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614 } +sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614, upload_time = "2025-01-14T19:03:36.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/45/4852afc5e093b76ba8f718d80fe1cc8604122a752806354379a7dbc41dc3/pyobjc_framework_DiscRecordingUI-11.0-py2.py3-none-any.whl", hash = "sha256:1af226c9350bb1d49960c02505e1e2f286e9377040dc2777a3f9a318925e081b", size = 4671 }, - { url = "https://files.pythonhosted.org/packages/98/01/c5645513eeaadf0b9e387849fa656fc22524a1881f0d3a44d5b78784f836/pyobjc_framework_DiscRecordingUI-11.0-py3-none-any.whl", hash = "sha256:943df030f497a5ab73e969a04df8a653138fb67ebcf2380fedb4b4886d4ffba0", size = 4736 }, + { url = "https://files.pythonhosted.org/packages/de/45/4852afc5e093b76ba8f718d80fe1cc8604122a752806354379a7dbc41dc3/pyobjc_framework_DiscRecordingUI-11.0-py2.py3-none-any.whl", hash = "sha256:1af226c9350bb1d49960c02505e1e2f286e9377040dc2777a3f9a318925e081b", size = 4671, upload_time = "2025-01-14T18:52:16.645Z" }, + { url = "https://files.pythonhosted.org/packages/98/01/c5645513eeaadf0b9e387849fa656fc22524a1881f0d3a44d5b78784f836/pyobjc_framework_DiscRecordingUI-11.0-py3-none-any.whl", hash = "sha256:943df030f497a5ab73e969a04df8a653138fb67ebcf2380fedb4b4886d4ffba0", size = 4736, upload_time = "2025-01-14T18:52:17.655Z" }, ] [[package]] @@ -2791,10 +2798,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096 } +sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096, upload_time = "2025-01-14T19:03:37.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/f4/f7ad86b2bb922b94745c369b90420cda984e6ad1ac9eb79ec32f5e332123/pyobjc_framework_DiskArbitration-11.0-py2.py3-none-any.whl", hash = "sha256:58823297eb09ff020ee156649170ab824fec32825bd32f2814c32e005920a72c", size = 4793 }, - { url = "https://files.pythonhosted.org/packages/8e/87/bf0fc2aa781a819421e572cf6315fae7d0baf46607f9a67c86525c7e0e03/pyobjc_framework_DiskArbitration-11.0-py3-none-any.whl", hash = "sha256:7d41189a2d82045a7195c4661d8ec16195b6325a2f68f9d960e9a9f6649d1131", size = 4865 }, + { url = "https://files.pythonhosted.org/packages/d2/f4/f7ad86b2bb922b94745c369b90420cda984e6ad1ac9eb79ec32f5e332123/pyobjc_framework_DiskArbitration-11.0-py2.py3-none-any.whl", hash = "sha256:58823297eb09ff020ee156649170ab824fec32825bd32f2814c32e005920a72c", size = 4793, upload_time = "2025-01-14T18:52:18.561Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/bf0fc2aa781a819421e572cf6315fae7d0baf46607f9a67c86525c7e0e03/pyobjc_framework_DiskArbitration-11.0-py3-none-any.whl", hash = "sha256:7d41189a2d82045a7195c4661d8ec16195b6325a2f68f9d960e9a9f6649d1131", size = 4865, upload_time = "2025-01-14T18:52:19.786Z" }, ] [[package]] @@ -2805,10 +2812,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798, upload_time = "2025-01-14T19:03:38.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/7f/6073ef2c5170abf55a15750cd069b0c3fdd03e48f3c86761a6a8ecaa0a38/pyobjc_framework_DVDPlayback-11.0-py2.py3-none-any.whl", hash = "sha256:2013289aa38166d81bcbf25d6600ead1996e50de2bc689e5cf36f36a45346424", size = 8171 }, - { url = "https://files.pythonhosted.org/packages/db/e4/97ed8d41491f366908581efb8644376fd81ede07ec2cf204cdb3c300ed1e/pyobjc_framework_DVDPlayback-11.0-py3-none-any.whl", hash = "sha256:c6be6ae410d8dce7179d6ee8c9bc421468d4b9c19af3ff0e59c93ae71cfc33e0", size = 8245 }, + { url = "https://files.pythonhosted.org/packages/6b/7f/6073ef2c5170abf55a15750cd069b0c3fdd03e48f3c86761a6a8ecaa0a38/pyobjc_framework_DVDPlayback-11.0-py2.py3-none-any.whl", hash = "sha256:2013289aa38166d81bcbf25d6600ead1996e50de2bc689e5cf36f36a45346424", size = 8171, upload_time = "2025-01-14T18:51:55.282Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/97ed8d41491f366908581efb8644376fd81ede07ec2cf204cdb3c300ed1e/pyobjc_framework_DVDPlayback-11.0-py3-none-any.whl", hash = "sha256:c6be6ae410d8dce7179d6ee8c9bc421468d4b9c19af3ff0e59c93ae71cfc33e0", size = 8245, upload_time = "2025-01-14T18:51:57.205Z" }, ] [[package]] @@ -2819,10 +2826,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399 } +sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399, upload_time = "2025-01-14T19:03:39.441Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/d5/e866c951237fb1b6423b85e1623a7f8cc417862261196e276ecc23141976/pyobjc_framework_EventKit-11.0-py2.py3-none-any.whl", hash = "sha256:934e31f4c82f887e1bf01f96d33de4c7c6727de3fdb55bc739e1c686c10cc151", size = 6717 }, - { url = "https://files.pythonhosted.org/packages/dc/47/3c0cc7b8c95e6759804b426e78510f65b8e7409c425b85f1b0109d14cdcc/pyobjc_framework_EventKit-11.0-py3-none-any.whl", hash = "sha256:5467977c79649dac9e0183dc72511f7dd49aab0260b67c2cfa25079a5a303f11", size = 6789 }, + { url = "https://files.pythonhosted.org/packages/97/d5/e866c951237fb1b6423b85e1623a7f8cc417862261196e276ecc23141976/pyobjc_framework_EventKit-11.0-py2.py3-none-any.whl", hash = "sha256:934e31f4c82f887e1bf01f96d33de4c7c6727de3fdb55bc739e1c686c10cc151", size = 6717, upload_time = "2025-01-14T18:52:20.684Z" }, + { url = "https://files.pythonhosted.org/packages/dc/47/3c0cc7b8c95e6759804b426e78510f65b8e7409c425b85f1b0109d14cdcc/pyobjc_framework_EventKit-11.0-py3-none-any.whl", hash = "sha256:5467977c79649dac9e0183dc72511f7dd49aab0260b67c2cfa25079a5a303f11", size = 6789, upload_time = "2025-01-14T18:52:21.73Z" }, ] [[package]] @@ -2833,10 +2840,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157 } +sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157, upload_time = "2025-01-14T19:03:40.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/9d/c25b0bc0d300dd5aedd61f0cbd94a91ec6608b550821108d554e9eea0ed7/pyobjc_framework_ExceptionHandling-11.0-py2.py3-none-any.whl", hash = "sha256:972e0a376fee4d3d4c5161f82a8e5f6305392dbf19e98c4c6486d737759ebd89", size = 6993 }, - { url = "https://files.pythonhosted.org/packages/cb/04/4b75e083325313e80e66f42d9a932c3febd2db48609d5d960a319b568f7c/pyobjc_framework_ExceptionHandling-11.0-py3-none-any.whl", hash = "sha256:d7f95fdb60a2636416066d3d12fad06cbf597e038576f8ed46fd3c742cc22252", size = 7063 }, + { url = "https://files.pythonhosted.org/packages/e7/9d/c25b0bc0d300dd5aedd61f0cbd94a91ec6608b550821108d554e9eea0ed7/pyobjc_framework_ExceptionHandling-11.0-py2.py3-none-any.whl", hash = "sha256:972e0a376fee4d3d4c5161f82a8e5f6305392dbf19e98c4c6486d737759ebd89", size = 6993, upload_time = "2025-01-14T18:52:22.621Z" }, + { url = "https://files.pythonhosted.org/packages/cb/04/4b75e083325313e80e66f42d9a932c3febd2db48609d5d960a319b568f7c/pyobjc_framework_ExceptionHandling-11.0-py3-none-any.whl", hash = "sha256:d7f95fdb60a2636416066d3d12fad06cbf597e038576f8ed46fd3c742cc22252", size = 7063, upload_time = "2025-01-14T18:52:24.447Z" }, ] [[package]] @@ -2847,10 +2854,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753, upload_time = "2025-01-14T19:03:42.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/03/a433c64c21c754ed796ae5ca0bad63fcb1d51134968ce0c53d4ee806ccd8/pyobjc_framework_ExecutionPolicy-11.0-py2.py3-none-any.whl", hash = "sha256:fdf78bf22fa6ea6f27b574f73856a8a22992d0c0d5a6ed64823e00000c06ffe7", size = 3668 }, - { url = "https://files.pythonhosted.org/packages/0b/47/da969dd9d56403e23cc95e68c4816563f64ed6fde7ff4e3c3710e8e8efcf/pyobjc_framework_ExecutionPolicy-11.0-py3-none-any.whl", hash = "sha256:d2dba6f3f7803d1cd0a5608a7ad75085b73097b6c3a935b7f1326c7202249751", size = 3737 }, + { url = "https://files.pythonhosted.org/packages/d5/03/a433c64c21c754ed796ae5ca0bad63fcb1d51134968ce0c53d4ee806ccd8/pyobjc_framework_ExecutionPolicy-11.0-py2.py3-none-any.whl", hash = "sha256:fdf78bf22fa6ea6f27b574f73856a8a22992d0c0d5a6ed64823e00000c06ffe7", size = 3668, upload_time = "2025-01-14T18:52:28.64Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/da969dd9d56403e23cc95e68c4816563f64ed6fde7ff4e3c3710e8e8efcf/pyobjc_framework_ExecutionPolicy-11.0-py3-none-any.whl", hash = "sha256:d2dba6f3f7803d1cd0a5608a7ad75085b73097b6c3a935b7f1326c7202249751", size = 3737, upload_time = "2025-01-14T18:52:30.841Z" }, ] [[package]] @@ -2861,10 +2868,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200 } +sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200, upload_time = "2025-01-14T19:03:43.188Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1d/ed580ce024d7e9a1ea88ee592d03b34f0b688414793bf8b7be5a367ecea8/pyobjc_framework_ExtensionKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98957dd51f0a4e02aa3d9d3a184f37ca5f99f4cb9e11282a2fc793d18de02af8", size = 7781 }, - { url = "https://files.pythonhosted.org/packages/fd/9e/a68989bf7bbba7b5fb1ade168d2179e37164439daaad63a27ccb790a6593/pyobjc_framework_ExtensionKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e341979ee4a7fc5978fe44d6d1d461c774411042cac4e119a32704d6c989de6f", size = 7783 }, + { url = "https://files.pythonhosted.org/packages/a7/1d/ed580ce024d7e9a1ea88ee592d03b34f0b688414793bf8b7be5a367ecea8/pyobjc_framework_ExtensionKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98957dd51f0a4e02aa3d9d3a184f37ca5f99f4cb9e11282a2fc793d18de02af8", size = 7781, upload_time = "2025-01-14T18:52:32.843Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9e/a68989bf7bbba7b5fb1ade168d2179e37164439daaad63a27ccb790a6593/pyobjc_framework_ExtensionKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e341979ee4a7fc5978fe44d6d1d461c774411042cac4e119a32704d6c989de6f", size = 7783, upload_time = "2025-01-14T18:52:33.711Z" }, ] [[package]] @@ -2875,10 +2882,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577 } +sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577, upload_time = "2025-01-14T19:03:44.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/96/bddfe9f72a59a3038ec3208a7d2a62332d5e171d7e3c338ccff6bd6e76b8/pyobjc_framework_ExternalAccessory-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:319f66edb96505f833fe7fe9ba810cb3b0d0c65605b8674bea52f040e8caebd6", size = 8785 }, - { url = "https://files.pythonhosted.org/packages/e7/e2/26e9cbb18723200ef71580e46c46f037b7feecc07cf50051cd6fcb426472/pyobjc_framework_ExternalAccessory-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:aaae920c9241d1b35a58ba76dba761689b248250d782179526f6dea151b1fda0", size = 8808 }, + { url = "https://files.pythonhosted.org/packages/a3/96/bddfe9f72a59a3038ec3208a7d2a62332d5e171d7e3c338ccff6bd6e76b8/pyobjc_framework_ExternalAccessory-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:319f66edb96505f833fe7fe9ba810cb3b0d0c65605b8674bea52f040e8caebd6", size = 8785, upload_time = "2025-01-14T18:52:40.529Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e2/26e9cbb18723200ef71580e46c46f037b7feecc07cf50051cd6fcb426472/pyobjc_framework_ExternalAccessory-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:aaae920c9241d1b35a58ba76dba761689b248250d782179526f6dea151b1fda0", size = 8808, upload_time = "2025-01-14T18:52:42.142Z" }, ] [[package]] @@ -2889,10 +2896,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701 } +sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701, upload_time = "2025-01-14T19:03:44.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/57/1f959ec54650d1afc08e89d2995a1534f44229b1371cf66429a45b27c32d/pyobjc_framework_FileProvider-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c7e803a37f7327c191a4de7dbb36e5fbf8bd08dadbcc7f626e491451c7a3849", size = 19179 }, - { url = "https://files.pythonhosted.org/packages/30/79/ff4dfe06eb43c97bd723f066ef2b92b00b1020206b4dcc5abe9b49746cad/pyobjc_framework_FileProvider-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d7acdc5e0f4b5488bcbf47d3eea469b22897a4b783fe3f5d4b2b1f3288e82038", size = 19154 }, + { url = "https://files.pythonhosted.org/packages/4a/57/1f959ec54650d1afc08e89d2995a1534f44229b1371cf66429a45b27c32d/pyobjc_framework_FileProvider-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c7e803a37f7327c191a4de7dbb36e5fbf8bd08dadbcc7f626e491451c7a3849", size = 19179, upload_time = "2025-01-14T18:53:01.659Z" }, + { url = "https://files.pythonhosted.org/packages/30/79/ff4dfe06eb43c97bd723f066ef2b92b00b1020206b4dcc5abe9b49746cad/pyobjc_framework_FileProvider-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d7acdc5e0f4b5488bcbf47d3eea469b22897a4b783fe3f5d4b2b1f3288e82038", size = 19154, upload_time = "2025-01-14T18:53:02.597Z" }, ] [[package]] @@ -2903,10 +2910,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-fileprovider" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419 } +sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419, upload_time = "2025-01-14T19:03:46.016Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/2e/8a91cfa9485a2e9ad295da8bb5505d0dc1046dec8557d2ae17eef75f3912/pyobjc_framework_FileProviderUI-11.0-py2.py3-none-any.whl", hash = "sha256:5102651febb5a6140f99b116b73d0fd6c9822372a5203506e4904ac0ebb1313c", size = 3642 }, - { url = "https://files.pythonhosted.org/packages/75/9b/a542159b1aefedb24f01440a929b7bbc6f4bbae3a74d09ad05a7f4adb9c0/pyobjc_framework_FileProviderUI-11.0-py3-none-any.whl", hash = "sha256:b75f70eef2af3696f3cb2e0de88bbb437343b53070078573ae72d64bf56fce9d", size = 3712 }, + { url = "https://files.pythonhosted.org/packages/0b/2e/8a91cfa9485a2e9ad295da8bb5505d0dc1046dec8557d2ae17eef75f3912/pyobjc_framework_FileProviderUI-11.0-py2.py3-none-any.whl", hash = "sha256:5102651febb5a6140f99b116b73d0fd6c9822372a5203506e4904ac0ebb1313c", size = 3642, upload_time = "2025-01-14T18:53:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/75/9b/a542159b1aefedb24f01440a929b7bbc6f4bbae3a74d09ad05a7f4adb9c0/pyobjc_framework_FileProviderUI-11.0-py3-none-any.whl", hash = "sha256:b75f70eef2af3696f3cb2e0de88bbb437343b53070078573ae72d64bf56fce9d", size = 3712, upload_time = "2025-01-14T18:53:07.403Z" }, ] [[package]] @@ -2917,10 +2924,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295, upload_time = "2025-01-14T19:03:46.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/f1/42797ae9065e0127df4b5bb7a45e06eff8568a476edbc8d590cea9d25228/pyobjc_framework_FinderSync-11.0-py2.py3-none-any.whl", hash = "sha256:cafb262d1ad1e3a86af333f673aeda4f9bdcf528ded97c2232fd1cf440d1db5a", size = 4788 }, - { url = "https://files.pythonhosted.org/packages/d8/96/2ed2ca5536f76102ea3bfb886cdc7b34ec51f53b122b9c535b4ac9b1ee03/pyobjc_framework_FinderSync-11.0-py3-none-any.whl", hash = "sha256:d00285b85038c5546e8566bec9cd3a4615708f0e6cb774d0ea804c69546ec915", size = 4860 }, + { url = "https://files.pythonhosted.org/packages/96/f1/42797ae9065e0127df4b5bb7a45e06eff8568a476edbc8d590cea9d25228/pyobjc_framework_FinderSync-11.0-py2.py3-none-any.whl", hash = "sha256:cafb262d1ad1e3a86af333f673aeda4f9bdcf528ded97c2232fd1cf440d1db5a", size = 4788, upload_time = "2025-01-14T18:53:09.559Z" }, + { url = "https://files.pythonhosted.org/packages/d8/96/2ed2ca5536f76102ea3bfb886cdc7b34ec51f53b122b9c535b4ac9b1ee03/pyobjc_framework_FinderSync-11.0-py3-none-any.whl", hash = "sha256:d00285b85038c5546e8566bec9cd3a4615708f0e6cb774d0ea804c69546ec915", size = 4860, upload_time = "2025-01-14T18:53:11.765Z" }, ] [[package]] @@ -2931,10 +2938,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078 } +sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078, upload_time = "2025-01-14T19:03:49.762Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/8a/75fd630865c9f9d69b1364208582872fc818b4c1a70fd9ae85a5cf7a2c5a/pyobjc_framework_FSEvents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0b3c7835251a35453e3079cf929b9e5329d02e2f4eaac2ebabbe19e1abd18ab", size = 13209 }, - { url = "https://files.pythonhosted.org/packages/19/c6/cae1a6a96ad493339e9f0f175bcf18c1526abe422b63309d873acd663dc2/pyobjc_framework_FSEvents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb8a5a7f7b5a70e15dae80672f10ecc16b5d1c1afe62ad2ccadb17a8098876cd", size = 13274 }, + { url = "https://files.pythonhosted.org/packages/1f/8a/75fd630865c9f9d69b1364208582872fc818b4c1a70fd9ae85a5cf7a2c5a/pyobjc_framework_FSEvents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0b3c7835251a35453e3079cf929b9e5329d02e2f4eaac2ebabbe19e1abd18ab", size = 13209, upload_time = "2025-01-14T18:52:49.478Z" }, + { url = "https://files.pythonhosted.org/packages/19/c6/cae1a6a96ad493339e9f0f175bcf18c1526abe422b63309d873acd663dc2/pyobjc_framework_FSEvents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb8a5a7f7b5a70e15dae80672f10ecc16b5d1c1afe62ad2ccadb17a8098876cd", size = 13274, upload_time = "2025-01-14T18:52:51.588Z" }, ] [[package]] @@ -2945,10 +2952,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459 } +sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459, upload_time = "2025-01-14T19:03:50.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/7e/8a41ab9880e415143baf771d55566e2a863ec538837480a5ee17e1ddc08b/pyobjc_framework_GameCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8bff2a36cf3cb52cbe321203147766e95997f881062143171cdd8ef2fde9e53", size = 18472 }, - { url = "https://files.pythonhosted.org/packages/c4/78/846aa21be2303cba955aaf781a362504a722183b8f6a030ba02f2b2073ad/pyobjc_framework_GameCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de57380e3b51579a6e8bc397c2bb5be5d0f6dcd4bf5abed587700cf7f57afd4", size = 18437 }, + { url = "https://files.pythonhosted.org/packages/1e/7e/8a41ab9880e415143baf771d55566e2a863ec538837480a5ee17e1ddc08b/pyobjc_framework_GameCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8bff2a36cf3cb52cbe321203147766e95997f881062143171cdd8ef2fde9e53", size = 18472, upload_time = "2025-01-14T18:53:13.794Z" }, + { url = "https://files.pythonhosted.org/packages/c4/78/846aa21be2303cba955aaf781a362504a722183b8f6a030ba02f2b2073ad/pyobjc_framework_GameCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de57380e3b51579a6e8bc397c2bb5be5d0f6dcd4bf5abed587700cf7f57afd4", size = 18437, upload_time = "2025-01-14T18:53:14.802Z" }, ] [[package]] @@ -2959,10 +2966,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318 } +sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318, upload_time = "2025-01-14T19:03:52.264Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/ec/05f356ab2d747a385c2a68908f2f67ee1b1e7a169b1497b0771b2226a174/pyobjc_framework_GameController-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:30e8f251be49ff67491df758c73149e7c7e87bee89919966ed1b2bf56fdaacf7", size = 20995 }, - { url = "https://files.pythonhosted.org/packages/66/b3/38319c9232e3508297bfedde700b125676845b1e27afe2bb681e8829f34a/pyobjc_framework_GameController-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:46403f23aaaf6a2e1a51e3954c53d6e910b80058117fdcf3a0a8100f25e30f07", size = 20919 }, + { url = "https://files.pythonhosted.org/packages/98/ec/05f356ab2d747a385c2a68908f2f67ee1b1e7a169b1497b0771b2226a174/pyobjc_framework_GameController-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:30e8f251be49ff67491df758c73149e7c7e87bee89919966ed1b2bf56fdaacf7", size = 20995, upload_time = "2025-01-14T18:53:19.979Z" }, + { url = "https://files.pythonhosted.org/packages/66/b3/38319c9232e3508297bfedde700b125676845b1e27afe2bb681e8829f34a/pyobjc_framework_GameController-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:46403f23aaaf6a2e1a51e3954c53d6e910b80058117fdcf3a0a8100f25e30f07", size = 20919, upload_time = "2025-01-14T18:53:20.943Z" }, ] [[package]] @@ -2974,10 +2981,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394, upload_time = "2025-01-14T19:03:53.762Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/4d/9fe843671c7b94d8e8a925662775d4b2632c138c6a0a9d1bb2c379f225c0/pyobjc_framework_GameKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dabe856c8638940d2b346abc7a1828cca12d00b47d2951d0ac9f4e27ecc8d3ec", size = 21667 }, - { url = "https://files.pythonhosted.org/packages/98/b2/d4d1f123fead83bf68eb4ecfab2125933f3114eaf2ed420d7bb99238ba67/pyobjc_framework_GameKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:40d506505f71ed57779c8be9b4e07ec9337c45aebe323b3f8dd8f8c75e6fce50", size = 21627 }, + { url = "https://files.pythonhosted.org/packages/88/4d/9fe843671c7b94d8e8a925662775d4b2632c138c6a0a9d1bb2c379f225c0/pyobjc_framework_GameKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dabe856c8638940d2b346abc7a1828cca12d00b47d2951d0ac9f4e27ecc8d3ec", size = 21667, upload_time = "2025-01-14T18:53:27.239Z" }, + { url = "https://files.pythonhosted.org/packages/98/b2/d4d1f123fead83bf68eb4ecfab2125933f3114eaf2ed420d7bb99238ba67/pyobjc_framework_GameKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:40d506505f71ed57779c8be9b4e07ec9337c45aebe323b3f8dd8f8c75e6fce50", size = 21627, upload_time = "2025-01-14T18:53:28.228Z" }, ] [[package]] @@ -2989,10 +2996,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-spritekit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837 } +sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837, upload_time = "2025-01-14T19:03:56.127Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/e7/3530071bf1897f2fe2e5f0c54620f0df9fcac586b9ba6bb5726fc9d295c2/pyobjc_framework_GameplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:01f59bbf5beb0cfcfea17011987995f1cccf2ec081d91269f95e71283dd83c67", size = 13381 }, - { url = "https://files.pythonhosted.org/packages/b9/07/075369dd9d4e3849646285d4083a9d28214fdd043b499c7929047b942c7f/pyobjc_framework_GameplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2d56af0a84439b3ddc64cdec90e3fab08b1d43da97bed0fb8d60714f47c4372", size = 13382 }, + { url = "https://files.pythonhosted.org/packages/30/e7/3530071bf1897f2fe2e5f0c54620f0df9fcac586b9ba6bb5726fc9d295c2/pyobjc_framework_GameplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:01f59bbf5beb0cfcfea17011987995f1cccf2ec081d91269f95e71283dd83c67", size = 13381, upload_time = "2025-01-14T18:53:34.217Z" }, + { url = "https://files.pythonhosted.org/packages/b9/07/075369dd9d4e3849646285d4083a9d28214fdd043b499c7929047b942c7f/pyobjc_framework_GameplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2d56af0a84439b3ddc64cdec90e3fab08b1d43da97bed0fb8d60714f47c4372", size = 13382, upload_time = "2025-01-14T18:53:36.303Z" }, ] [[package]] @@ -3003,10 +3010,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558 } +sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558, upload_time = "2025-01-14T19:03:57.117Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/66/36a2fa7ef61b54a8e283355518ed003aa28b26e1dfad9ecbb7f543a08acd/pyobjc_framework_HealthKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ee87453c28bd4040b12a3bc834f0ea1989e331400845a14079e8f4a6ede70aa2", size = 20139 }, - { url = "https://files.pythonhosted.org/packages/5f/fd/95d40483d9d185317adbf8433d0c7e83ba36ec6c5a824159b87160f6cebe/pyobjc_framework_HealthKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:680da6d67b0c79d15e897f1c588a8b02d780573aef3692e982294c43727eecf3", size = 20163 }, + { url = "https://files.pythonhosted.org/packages/f0/66/36a2fa7ef61b54a8e283355518ed003aa28b26e1dfad9ecbb7f543a08acd/pyobjc_framework_HealthKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ee87453c28bd4040b12a3bc834f0ea1989e331400845a14079e8f4a6ede70aa2", size = 20139, upload_time = "2025-01-14T18:53:44.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/fd/95d40483d9d185317adbf8433d0c7e83ba36ec6c5a824159b87160f6cebe/pyobjc_framework_HealthKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:680da6d67b0c79d15e897f1c588a8b02d780573aef3692e982294c43727eecf3", size = 20163, upload_time = "2025-01-14T18:53:45.278Z" }, ] [[package]] @@ -3017,10 +3024,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935 } +sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935, upload_time = "2025-01-14T19:03:58.548Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/fb/29f20521e0df5da0110f1d6a48e4ed3530a2c0b670bf62d89ceeddd42c18/pyobjc_framework_ImageCaptureCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3fd78aa4a69e24caed38ae17a69b973e505324d966df86b47441318800a52db9", size = 16611 }, - { url = "https://files.pythonhosted.org/packages/0e/ce/404666e27318435a0513dcf64b85d7cd99195b2e822e03796b03af549c52/pyobjc_framework_ImageCaptureCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5cc6c6acbfca05977adc0e339e1225d5cd314af2fa455a70baebb54f9fb2b64", size = 16636 }, + { url = "https://files.pythonhosted.org/packages/e2/fb/29f20521e0df5da0110f1d6a48e4ed3530a2c0b670bf62d89ceeddd42c18/pyobjc_framework_ImageCaptureCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3fd78aa4a69e24caed38ae17a69b973e505324d966df86b47441318800a52db9", size = 16611, upload_time = "2025-01-14T18:54:05.308Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ce/404666e27318435a0513dcf64b85d7cd99195b2e822e03796b03af549c52/pyobjc_framework_ImageCaptureCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5cc6c6acbfca05977adc0e339e1225d5cd314af2fa455a70baebb54f9fb2b64", size = 16636, upload_time = "2025-01-14T18:54:06.288Z" }, ] [[package]] @@ -3031,10 +3038,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684 } +sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684, upload_time = "2025-01-14T19:03:59.525Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/08/18572def66bf1e0ee6d079b45b34f8b4cbf2ab40b3024c351e4bd83cfa4c/pyobjc_framework_InputMethodKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a58273233e236cb9fa2d8d9295017c6bf26d6f47cc3a5dc9ba81f1c1e64a346", size = 9369 }, - { url = "https://files.pythonhosted.org/packages/9d/c9/7793b0d7b363548e62499660899893dff2953ae3a56aa5080e9b199d1291/pyobjc_framework_InputMethodKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7869db2b08586e97181ec2b60b8f5b9d3a683097bae4ce4bb29dc3c5709c3f13", size = 9390 }, + { url = "https://files.pythonhosted.org/packages/b5/08/18572def66bf1e0ee6d079b45b34f8b4cbf2ab40b3024c351e4bd83cfa4c/pyobjc_framework_InputMethodKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a58273233e236cb9fa2d8d9295017c6bf26d6f47cc3a5dc9ba81f1c1e64a346", size = 9369, upload_time = "2025-01-14T18:54:12.452Z" }, + { url = "https://files.pythonhosted.org/packages/9d/c9/7793b0d7b363548e62499660899893dff2953ae3a56aa5080e9b199d1291/pyobjc_framework_InputMethodKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7869db2b08586e97181ec2b60b8f5b9d3a683097bae4ce4bb29dc3c5709c3f13", size = 9390, upload_time = "2025-01-14T18:54:13.383Z" }, ] [[package]] @@ -3045,10 +3052,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687, upload_time = "2025-01-14T19:04:00.515Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/db/0f3334648a53c8ad663fd19d5421863cb0b711e38a2eb742798d50ed33ef/pyobjc_framework_InstallerPlugins-11.0-py2.py3-none-any.whl", hash = "sha256:cb21bfd5597233a2de3d8c0a8d50f23cf92c43e8963edf85787430ac3cadf4a3", size = 4716 }, - { url = "https://files.pythonhosted.org/packages/f7/56/fe6f50d74d19b0f85035aba977db7039eedbd2de5ac991278a6a5be475a0/pyobjc_framework_InstallerPlugins-11.0-py3-none-any.whl", hash = "sha256:2221301f466d30d6fd32c7317560c85926a3ee93f1de52d320e3b3cd826a8f93", size = 4784 }, + { url = "https://files.pythonhosted.org/packages/03/db/0f3334648a53c8ad663fd19d5421863cb0b711e38a2eb742798d50ed33ef/pyobjc_framework_InstallerPlugins-11.0-py2.py3-none-any.whl", hash = "sha256:cb21bfd5597233a2de3d8c0a8d50f23cf92c43e8963edf85787430ac3cadf4a3", size = 4716, upload_time = "2025-01-14T18:54:17.885Z" }, + { url = "https://files.pythonhosted.org/packages/f7/56/fe6f50d74d19b0f85035aba977db7039eedbd2de5ac991278a6a5be475a0/pyobjc_framework_InstallerPlugins-11.0-py3-none-any.whl", hash = "sha256:2221301f466d30d6fd32c7317560c85926a3ee93f1de52d320e3b3cd826a8f93", size = 4784, upload_time = "2025-01-14T18:54:19.028Z" }, ] [[package]] @@ -3060,10 +3067,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027 } +sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027, upload_time = "2025-01-14T19:04:01.405Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/41/4c0ec3d59f9930e9c52570f7e26d79055881e0009e07466b4988c107ef7c/pyobjc_framework_InstantMessage-11.0-py2.py3-none-any.whl", hash = "sha256:ce364e4e18ec8551512b7d968c0d950ccf7de4bb470f66fe524f3bc8d23df0d1", size = 5334 }, - { url = "https://files.pythonhosted.org/packages/19/d9/e3620a5316c986b27361d2f21dd74b48f70c6f7bfe580075e970ca9d7bd6/pyobjc_framework_InstantMessage-11.0-py3-none-any.whl", hash = "sha256:a2817353eaf8f37fe6063c28006b2a0889892e3de801b51b059c153a9d3f35f8", size = 5402 }, + { url = "https://files.pythonhosted.org/packages/8c/41/4c0ec3d59f9930e9c52570f7e26d79055881e0009e07466b4988c107ef7c/pyobjc_framework_InstantMessage-11.0-py2.py3-none-any.whl", hash = "sha256:ce364e4e18ec8551512b7d968c0d950ccf7de4bb470f66fe524f3bc8d23df0d1", size = 5334, upload_time = "2025-01-14T18:54:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/19/d9/e3620a5316c986b27361d2f21dd74b48f70c6f7bfe580075e970ca9d7bd6/pyobjc_framework_InstantMessage-11.0-py3-none-any.whl", hash = "sha256:a2817353eaf8f37fe6063c28006b2a0889892e3de801b51b059c153a9d3f35f8", size = 5402, upload_time = "2025-01-14T18:54:21.261Z" }, ] [[package]] @@ -3074,10 +3081,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921 } +sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921, upload_time = "2025-01-14T19:04:02.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/2e/cd8a4aa10a1d3808dd6f71195a28906c573345673dd92b774fbb8c93dd75/pyobjc_framework_Intents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9a445a3d4a0622ebf96c65b0ac0be7cec1e72cf7fd9900cd5ace6acf4e84bce", size = 32021 }, - { url = "https://files.pythonhosted.org/packages/4a/0e/05c457dab601e3eb5ed7243a04fede32423f08dd03a08e988611359d55b4/pyobjc_framework_Intents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1e148accce2c7c9243ff90ab3f1a200f93d93506da9c3a2cd034fd5579cb839a", size = 32008 }, + { url = "https://files.pythonhosted.org/packages/86/2e/cd8a4aa10a1d3808dd6f71195a28906c573345673dd92b774fbb8c93dd75/pyobjc_framework_Intents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9a445a3d4a0622ebf96c65b0ac0be7cec1e72cf7fd9900cd5ace6acf4e84bce", size = 32021, upload_time = "2025-01-14T18:54:23.325Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0e/05c457dab601e3eb5ed7243a04fede32423f08dd03a08e988611359d55b4/pyobjc_framework_Intents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1e148accce2c7c9243ff90ab3f1a200f93d93506da9c3a2cd034fd5579cb839a", size = 32008, upload_time = "2025-01-14T18:54:24.317Z" }, ] [[package]] @@ -3088,10 +3095,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-intents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774, upload_time = "2025-01-14T19:04:03.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/19/f32a14585e749258bb945805da93fd71e05534b14e09fab243fb5ec507ff/pyobjc_framework_IntentsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c677225d38fffc5e00df803f93a6a627c466b35a362ed27173f7901e185882e", size = 8772 }, - { url = "https://files.pythonhosted.org/packages/2f/d4/e81e9cfafef63cef481ab251a961ca98e176ca244be91368e0f6b6fe8793/pyobjc_framework_IntentsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b93a1d9594f471596f255db354c13d67caed7aa020afb9f4e69cde2674f4db71", size = 8789 }, + { url = "https://files.pythonhosted.org/packages/89/19/f32a14585e749258bb945805da93fd71e05534b14e09fab243fb5ec507ff/pyobjc_framework_IntentsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c677225d38fffc5e00df803f93a6a627c466b35a362ed27173f7901e185882e", size = 8772, upload_time = "2025-01-14T18:54:29.958Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d4/e81e9cfafef63cef481ab251a961ca98e176ca244be91368e0f6b6fe8793/pyobjc_framework_IntentsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b93a1d9594f471596f255db354c13d67caed7aa020afb9f4e69cde2674f4db71", size = 8789, upload_time = "2025-01-14T18:54:30.984Z" }, ] [[package]] @@ -3102,10 +3109,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463 } +sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463, upload_time = "2025-01-14T19:04:04.582Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/2e/2037b1c3459008ccdc41d65ab236d7919eed9bbadd0f02f65dc0193bb170/pyobjc_framework_IOBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7d2005d3eff2afed4b5930613ae3c1b50004ebabffb86c0d5dd28d54436e16e6", size = 41010 }, - { url = "https://files.pythonhosted.org/packages/2a/52/c266636ff3edc98c1aaf2cc154392876a68d4167bed0351dc2933d5ccc3c/pyobjc_framework_IOBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f86d2e675ee2a61ba3d2a446322e918e8ef2dc3e242e893ef81abfc480a6f2c2", size = 41012 }, + { url = "https://files.pythonhosted.org/packages/7a/2e/2037b1c3459008ccdc41d65ab236d7919eed9bbadd0f02f65dc0193bb170/pyobjc_framework_IOBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7d2005d3eff2afed4b5930613ae3c1b50004ebabffb86c0d5dd28d54436e16e6", size = 41010, upload_time = "2025-01-14T18:53:51.62Z" }, + { url = "https://files.pythonhosted.org/packages/2a/52/c266636ff3edc98c1aaf2cc154392876a68d4167bed0351dc2933d5ccc3c/pyobjc_framework_IOBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f86d2e675ee2a61ba3d2a446322e918e8ef2dc3e242e893ef81abfc480a6f2c2", size = 41012, upload_time = "2025-01-14T18:53:52.757Z" }, ] [[package]] @@ -3116,10 +3123,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-iobluetooth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091 } +sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091, upload_time = "2025-01-14T19:04:05.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/75/9401ae099f32a6be2e5759f8d25c573bcf103833343457ca5981153262ab/pyobjc_framework_IOBluetoothUI-11.0-py2.py3-none-any.whl", hash = "sha256:0f94afeb5ecbde07712ea7658a38d6b0e3558154a6bc29c9a33b633f5952b2c3", size = 3972 }, - { url = "https://files.pythonhosted.org/packages/11/a3/75e473de9d25084bfbfa4c0ba24edf038956a604d78219894dc0b412e501/pyobjc_framework_IOBluetoothUI-11.0-py3-none-any.whl", hash = "sha256:5bc366a9904532168ac2c49523e7f090f81b6acbb7b8929ffc7855be0b1d4cf7", size = 4043 }, + { url = "https://files.pythonhosted.org/packages/d9/75/9401ae099f32a6be2e5759f8d25c573bcf103833343457ca5981153262ab/pyobjc_framework_IOBluetoothUI-11.0-py2.py3-none-any.whl", hash = "sha256:0f94afeb5ecbde07712ea7658a38d6b0e3558154a6bc29c9a33b633f5952b2c3", size = 3972, upload_time = "2025-01-14T18:53:57.925Z" }, + { url = "https://files.pythonhosted.org/packages/11/a3/75e473de9d25084bfbfa4c0ba24edf038956a604d78219894dc0b412e501/pyobjc_framework_IOBluetoothUI-11.0-py3-none-any.whl", hash = "sha256:5bc366a9904532168ac2c49523e7f090f81b6acbb7b8929ffc7855be0b1d4cf7", size = 4043, upload_time = "2025-01-14T18:54:00.086Z" }, ] [[package]] @@ -3130,10 +3137,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555, upload_time = "2025-01-14T19:04:09.475Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/08/b96f84b623e2dd2ef733ccdd67a1694f51bfdb4dfd81d38e7755566ab9e5/pyobjc_framework_IOSurface-11.0-py2.py3-none-any.whl", hash = "sha256:58c6e79401a00dc63a5797cd3cc067542d4f94fcd2fc8979dc248c3b06c3b829", size = 4905 }, - { url = "https://files.pythonhosted.org/packages/2d/af/4d7ece43c993369a8593c36e0f239b739b78c01e71d74553a630dadd1599/pyobjc_framework_IOSurface-11.0-py3-none-any.whl", hash = "sha256:f2bc13cbfd178396bde6e7558b05a49f69cce376885a07f645a5dd69d2b578fc", size = 4972 }, + { url = "https://files.pythonhosted.org/packages/b8/08/b96f84b623e2dd2ef733ccdd67a1694f51bfdb4dfd81d38e7755566ab9e5/pyobjc_framework_IOSurface-11.0-py2.py3-none-any.whl", hash = "sha256:58c6e79401a00dc63a5797cd3cc067542d4f94fcd2fc8979dc248c3b06c3b829", size = 4905, upload_time = "2025-01-14T18:54:00.978Z" }, + { url = "https://files.pythonhosted.org/packages/2d/af/4d7ece43c993369a8593c36e0f239b739b78c01e71d74553a630dadd1599/pyobjc_framework_IOSurface-11.0-py3-none-any.whl", hash = "sha256:f2bc13cbfd178396bde6e7558b05a49f69cce376885a07f645a5dd69d2b578fc", size = 4972, upload_time = "2025-01-14T18:54:03.244Z" }, ] [[package]] @@ -3144,10 +3151,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647 } +sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647, upload_time = "2025-01-14T19:04:11.333Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/d2/52d1c71ec91ec299e1324658d023954cf62ce4c275155dc66cd298517ae2/pyobjc_framework_iTunesLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3836fccec315f5186e4b029b486fd18d4b1f24a4c2e73f2d9f3e157ee66d294d", size = 5147 }, - { url = "https://files.pythonhosted.org/packages/dc/97/c23c522d506ae01740c04982a1db5861888056dc65d56876a2de0fc490bc/pyobjc_framework_iTunesLibrary-11.0-py3-none-any.whl", hash = "sha256:bfd40fde3f057318329e5fb6e256051eea3f6cd2e2adb9c1f1f51fcb87deb05a", size = 5210 }, + { url = "https://files.pythonhosted.org/packages/5f/d2/52d1c71ec91ec299e1324658d023954cf62ce4c275155dc66cd298517ae2/pyobjc_framework_iTunesLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3836fccec315f5186e4b029b486fd18d4b1f24a4c2e73f2d9f3e157ee66d294d", size = 5147, upload_time = "2025-01-14T19:01:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/dc/97/c23c522d506ae01740c04982a1db5861888056dc65d56876a2de0fc490bc/pyobjc_framework_iTunesLibrary-11.0-py3-none-any.whl", hash = "sha256:bfd40fde3f057318329e5fb6e256051eea3f6cd2e2adb9c1f1f51fcb87deb05a", size = 5210, upload_time = "2025-01-14T19:01:51.573Z" }, ] [[package]] @@ -3158,10 +3165,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794 } +sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794, upload_time = "2025-01-14T19:04:14.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/fe/ad7278325d8c760d5366b08d6162193612a3bf33bb0fa98d83d7dcc41918/pyobjc_framework_KernelManagement-11.0-py2.py3-none-any.whl", hash = "sha256:e2ad0efd00c0dce90fc05efac296733282c482d54ec7c5fdcb86b4fb8dff1eb8", size = 3604 }, - { url = "https://files.pythonhosted.org/packages/1e/20/8aff6699bf780c88770214f72e92b9db736de078aa1aaaea45312758116e/pyobjc_framework_KernelManagement-11.0-py3-none-any.whl", hash = "sha256:90baacf8bea2883fd62ffb5d7dc6e6ae43fcc6f444458c884da8d92170fcaa5e", size = 3675 }, + { url = "https://files.pythonhosted.org/packages/ee/fe/ad7278325d8c760d5366b08d6162193612a3bf33bb0fa98d83d7dcc41918/pyobjc_framework_KernelManagement-11.0-py2.py3-none-any.whl", hash = "sha256:e2ad0efd00c0dce90fc05efac296733282c482d54ec7c5fdcb86b4fb8dff1eb8", size = 3604, upload_time = "2025-01-14T18:54:36.643Z" }, + { url = "https://files.pythonhosted.org/packages/1e/20/8aff6699bf780c88770214f72e92b9db736de078aa1aaaea45312758116e/pyobjc_framework_KernelManagement-11.0-py3-none-any.whl", hash = "sha256:90baacf8bea2883fd62ffb5d7dc6e6ae43fcc6f444458c884da8d92170fcaa5e", size = 3675, upload_time = "2025-01-14T18:54:37.62Z" }, ] [[package]] @@ -3172,10 +3179,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704 } +sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704, upload_time = "2025-01-14T19:04:14.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/87/a8d2f508c021afa4f8af51773ab22cbd883270bfda8368a86d473736b05a/pyobjc_framework_LatentSemanticMapping-11.0-py2.py3-none-any.whl", hash = "sha256:87fd91320fb7ce0b2c482fda41a5c38388f5a694ee2d7208725d22ff75438c00", size = 5369 }, - { url = "https://files.pythonhosted.org/packages/df/f0/cea2a0d25ad20aef6eb38c432d2c93bda2cb2239c6286b6086f8687a8072/pyobjc_framework_LatentSemanticMapping-11.0-py3-none-any.whl", hash = "sha256:073b8a4e7a22e6abd58005b7d7091144aec4fc1d4b519e9f972b3aee9da30009", size = 5435 }, + { url = "https://files.pythonhosted.org/packages/7f/87/a8d2f508c021afa4f8af51773ab22cbd883270bfda8368a86d473736b05a/pyobjc_framework_LatentSemanticMapping-11.0-py2.py3-none-any.whl", hash = "sha256:87fd91320fb7ce0b2c482fda41a5c38388f5a694ee2d7208725d22ff75438c00", size = 5369, upload_time = "2025-01-14T18:54:38.493Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/cea2a0d25ad20aef6eb38c432d2c93bda2cb2239c6286b6086f8687a8072/pyobjc_framework_LatentSemanticMapping-11.0-py3-none-any.whl", hash = "sha256:073b8a4e7a22e6abd58005b7d7091144aec4fc1d4b519e9f972b3aee9da30009", size = 5435, upload_time = "2025-01-14T18:54:39.643Z" }, ] [[package]] @@ -3186,10 +3193,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-coreservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691 } +sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691, upload_time = "2025-01-14T19:04:15.884Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/46/72937390e3eb0f31809f0d56004a388d20b49724495885e8be677707c07c/pyobjc_framework_LaunchServices-11.0-py2.py3-none-any.whl", hash = "sha256:654572e5f2997d8f802b97f619fc6c7d4f927abb03ce53b3dad89b376517b2d1", size = 3807 }, - { url = "https://files.pythonhosted.org/packages/c0/12/74b96f187beb2f5605f9d487c3141ac8d25193556f2f5febff3580e8b2cb/pyobjc_framework_LaunchServices-11.0-py3-none-any.whl", hash = "sha256:dbc169442deae53f881d1d07fc79c9da6459e5f0b411e8dd1cfd1c519b3a99c8", size = 3876 }, + { url = "https://files.pythonhosted.org/packages/35/46/72937390e3eb0f31809f0d56004a388d20b49724495885e8be677707c07c/pyobjc_framework_LaunchServices-11.0-py2.py3-none-any.whl", hash = "sha256:654572e5f2997d8f802b97f619fc6c7d4f927abb03ce53b3dad89b376517b2d1", size = 3807, upload_time = "2025-01-14T18:54:40.579Z" }, + { url = "https://files.pythonhosted.org/packages/c0/12/74b96f187beb2f5605f9d487c3141ac8d25193556f2f5febff3580e8b2cb/pyobjc_framework_LaunchServices-11.0-py3-none-any.whl", hash = "sha256:dbc169442deae53f881d1d07fc79c9da6459e5f0b411e8dd1cfd1c519b3a99c8", size = 3876, upload_time = "2025-01-14T18:54:41.577Z" }, ] [[package]] @@ -3200,10 +3207,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496 } +sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496, upload_time = "2025-01-14T19:04:16.82Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/1f/f3273cc8261d45a6bef1fa48ac39cd94f6a1e77b1ec70f79bae52ad54015/pyobjc_framework_libdispatch-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cebdc33a1a771c9ab03fe5c8a2b0ed9698804e7bccdbfcd3cc0045c4b4aad4f3", size = 20607 }, - { url = "https://files.pythonhosted.org/packages/32/08/40638a5e916b1b94b4b29abacb18628fd47871d80fdf2fc1ef7216726d29/pyobjc_framework_libdispatch-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:999815af50ad2216e28d76893023b7839b7f1e8f22bcf7062d81d9a51ade4613", size = 15949 }, + { url = "https://files.pythonhosted.org/packages/24/1f/f3273cc8261d45a6bef1fa48ac39cd94f6a1e77b1ec70f79bae52ad54015/pyobjc_framework_libdispatch-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cebdc33a1a771c9ab03fe5c8a2b0ed9698804e7bccdbfcd3cc0045c4b4aad4f3", size = 20607, upload_time = "2025-01-14T19:01:53.577Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/40638a5e916b1b94b4b29abacb18628fd47871d80fdf2fc1ef7216726d29/pyobjc_framework_libdispatch-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:999815af50ad2216e28d76893023b7839b7f1e8f22bcf7062d81d9a51ade4613", size = 15949, upload_time = "2025-01-14T19:01:54.496Z" }, ] [[package]] @@ -3214,10 +3221,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627, upload_time = "2025-01-14T19:04:17.728Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/c2/b77019e344b3f46ca4169c19e0539cff9586c8db0a97715590696993bd00/pyobjc_framework_libxpc-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5f05f9eb3662df5832ff09ab788d6f6099f4674cb015200db317ea8c69f8c5e8", size = 19683 }, - { url = "https://files.pythonhosted.org/packages/3c/b9/bf34709c2d8f62a029f4c8e7f9a58c6eb5f3a68542cbcd2a15070b66485a/pyobjc_framework_libxpc-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e000cad8588a961a3e6e5016736cd76b5d992b080cfe8b95745691db5a0ce8df", size = 19788 }, + { url = "https://files.pythonhosted.org/packages/21/c2/b77019e344b3f46ca4169c19e0539cff9586c8db0a97715590696993bd00/pyobjc_framework_libxpc-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5f05f9eb3662df5832ff09ab788d6f6099f4674cb015200db317ea8c69f8c5e8", size = 19683, upload_time = "2025-01-14T19:02:02.364Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b9/bf34709c2d8f62a029f4c8e7f9a58c6eb5f3a68542cbcd2a15070b66485a/pyobjc_framework_libxpc-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e000cad8588a961a3e6e5016736cd76b5d992b080cfe8b95745691db5a0ce8df", size = 19788, upload_time = "2025-01-14T19:02:03.327Z" }, ] [[package]] @@ -3229,10 +3236,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231 } +sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231, upload_time = "2025-01-14T19:04:20.763Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/fc/aa3f0016e2246c4574cce0e323416303992411a012266b5bdda74095ebef/pyobjc_framework_LinkPresentation-11.0-py2.py3-none-any.whl", hash = "sha256:c10ee1ac48bb7cd2d67ade7f354ec71af1f4244a8deb8530ba646fd4ba327b21", size = 3799 }, - { url = "https://files.pythonhosted.org/packages/85/0b/77c16f2d4541a4490723e18c03c3bd6ecf7db789cf4988e628753e2e4526/pyobjc_framework_LinkPresentation-11.0-py3-none-any.whl", hash = "sha256:5b063900715c5bcf58f533e6c9672473cb07fe3eaa0f0454d93947defa09f13e", size = 3865 }, + { url = "https://files.pythonhosted.org/packages/12/fc/aa3f0016e2246c4574cce0e323416303992411a012266b5bdda74095ebef/pyobjc_framework_LinkPresentation-11.0-py2.py3-none-any.whl", hash = "sha256:c10ee1ac48bb7cd2d67ade7f354ec71af1f4244a8deb8530ba646fd4ba327b21", size = 3799, upload_time = "2025-01-14T18:54:43.137Z" }, + { url = "https://files.pythonhosted.org/packages/85/0b/77c16f2d4541a4490723e18c03c3bd6ecf7db789cf4988e628753e2e4526/pyobjc_framework_LinkPresentation-11.0-py3-none-any.whl", hash = "sha256:5b063900715c5bcf58f533e6c9672473cb07fe3eaa0f0454d93947defa09f13e", size = 3865, upload_time = "2025-01-14T18:54:44.287Z" }, ] [[package]] @@ -3244,10 +3251,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-security" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020, upload_time = "2025-01-14T19:04:22.206Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/dd/eaa44e4fe3b5c312190c0468afcab0a4372da29535fe9f860b6b9e1e6b4a/pyobjc_framework_LocalAuthentication-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6500bb5b195799d70f2a622d89a9c2531cb13d6afe30916cf073a195dd86eb", size = 10515 }, - { url = "https://files.pythonhosted.org/packages/31/86/f4e913e966a6dbefbaa95aed35e7d235ba2f172d079d3c0b4351a584357b/pyobjc_framework_LocalAuthentication-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c0291e743fb1534c1df900e9adacc809af0651744627ce8ae25cfd021e3db73b", size = 10530 }, + { url = "https://files.pythonhosted.org/packages/d1/dd/eaa44e4fe3b5c312190c0468afcab0a4372da29535fe9f860b6b9e1e6b4a/pyobjc_framework_LocalAuthentication-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6500bb5b195799d70f2a622d89a9c2531cb13d6afe30916cf073a195dd86eb", size = 10515, upload_time = "2025-01-14T18:54:46.214Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/f4e913e966a6dbefbaa95aed35e7d235ba2f172d079d3c0b4351a584357b/pyobjc_framework_LocalAuthentication-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c0291e743fb1534c1df900e9adacc809af0651744627ce8ae25cfd021e3db73b", size = 10530, upload_time = "2025-01-14T18:54:47.16Z" }, ] [[package]] @@ -3259,10 +3266,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-localauthentication" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204, upload_time = "2025-01-14T19:04:23.566Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/66/2151e5ee7fb97b34c7eda9f8b1442683cced27bcb273d34c8aa2c564e528/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py2.py3-none-any.whl", hash = "sha256:0ccbbdd8c7142b1670885881c803f684ee356df83a5338be9135f46462caae6c", size = 3914 }, - { url = "https://files.pythonhosted.org/packages/d8/a9/c362ac3586bb2d46868b8ea9da3747c9aae3f0c9448ee09934a1be805383/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py3-none-any.whl", hash = "sha256:e8da98dc38a88995e344742585d3735af9b5bd9926a29774d77e2aa6dd46b7af", size = 3984 }, + { url = "https://files.pythonhosted.org/packages/da/66/2151e5ee7fb97b34c7eda9f8b1442683cced27bcb273d34c8aa2c564e528/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py2.py3-none-any.whl", hash = "sha256:0ccbbdd8c7142b1670885881c803f684ee356df83a5338be9135f46462caae6c", size = 3914, upload_time = "2025-01-14T18:54:52.074Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a9/c362ac3586bb2d46868b8ea9da3747c9aae3f0c9448ee09934a1be805383/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py3-none-any.whl", hash = "sha256:e8da98dc38a88995e344742585d3735af9b5bd9926a29774d77e2aa6dd46b7af", size = 3984, upload_time = "2025-01-14T18:54:54.974Z" }, ] [[package]] @@ -3273,10 +3280,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274 } +sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274, upload_time = "2025-01-14T19:04:25.086Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/38/f9bcd204c1ba0943365f3cc505d934ea93fe4b99d61e961ced0f0991a4f9/pyobjc_framework_MailKit-11.0-py2.py3-none-any.whl", hash = "sha256:78e54ff3988fd1af16c06e0c39dea3b7ff522e367d262f58e88962772291c7f9", size = 4803 }, - { url = "https://files.pythonhosted.org/packages/64/4a/f3596583795c608838c7fa84fc4836f365c5744a3e412392d47a200a6221/pyobjc_framework_MailKit-11.0-py3-none-any.whl", hash = "sha256:0573ee0be66419130774aca36b611d0d07fcf7c756524860acba8fe17eefeec2", size = 4874 }, + { url = "https://files.pythonhosted.org/packages/d2/38/f9bcd204c1ba0943365f3cc505d934ea93fe4b99d61e961ced0f0991a4f9/pyobjc_framework_MailKit-11.0-py2.py3-none-any.whl", hash = "sha256:78e54ff3988fd1af16c06e0c39dea3b7ff522e367d262f58e88962772291c7f9", size = 4803, upload_time = "2025-01-14T18:54:58.295Z" }, + { url = "https://files.pythonhosted.org/packages/64/4a/f3596583795c608838c7fa84fc4836f365c5744a3e412392d47a200a6221/pyobjc_framework_MailKit-11.0-py3-none-any.whl", hash = "sha256:0573ee0be66419130774aca36b611d0d07fcf7c756524860acba8fe17eefeec2", size = 4874, upload_time = "2025-01-14T18:55:00.648Z" }, ] [[package]] @@ -3289,10 +3296,10 @@ dependencies = [ { name = "pyobjc-framework-corelocation" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080 } +sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080, upload_time = "2025-01-14T19:04:26.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/7e/f0457c7ca001a01f47aa944c1f86a24d2d04db0aa1c19f51cbf77a65cc9b/pyobjc_framework_MapKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83128d79aa7644e5b966b32346f7da749b1dbb110dadba857b93ecf5663e24e6", size = 23045 }, - { url = "https://files.pythonhosted.org/packages/d5/b0/532b4f57f8783cf6394b17e76174c393d0503ee41e026782a9950bd46279/pyobjc_framework_MapKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e6aa1d00cfe2e02b301467e24ca51e469e9a8a2ec2a9f097b73adca1a5a2a054", size = 23040 }, + { url = "https://files.pythonhosted.org/packages/fc/7e/f0457c7ca001a01f47aa944c1f86a24d2d04db0aa1c19f51cbf77a65cc9b/pyobjc_framework_MapKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83128d79aa7644e5b966b32346f7da749b1dbb110dadba857b93ecf5663e24e6", size = 23045, upload_time = "2025-01-14T18:55:02.629Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b0/532b4f57f8783cf6394b17e76174c393d0503ee41e026782a9950bd46279/pyobjc_framework_MapKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e6aa1d00cfe2e02b301467e24ca51e469e9a8a2ec2a9f097b73adca1a5a2a054", size = 23040, upload_time = "2025-01-14T18:55:04.055Z" }, ] [[package]] @@ -3303,10 +3310,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671 } +sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671, upload_time = "2025-01-14T19:04:27.624Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/1f/36b1115cfd02d68d39cc3fe976fe3d40bad1d1a0a9c8175c66d230bb7276/pyobjc_framework_MediaAccessibility-11.0-py2.py3-none-any.whl", hash = "sha256:901961f171f7af184decbf5a3899debfa56dbd1a63a53d0ff3d93eff90f2f464", size = 4637 }, - { url = "https://files.pythonhosted.org/packages/72/3f/fa350681a6599ed6756dc598fcd17fda1521249e4570a57b4a9b9c900f47/pyobjc_framework_MediaAccessibility-11.0-py3-none-any.whl", hash = "sha256:3f4b9e4d1ac8e7f8cdb7a2e9839ab75cb358dead3e6365ccd8d6017d7e93811e", size = 4708 }, + { url = "https://files.pythonhosted.org/packages/50/1f/36b1115cfd02d68d39cc3fe976fe3d40bad1d1a0a9c8175c66d230bb7276/pyobjc_framework_MediaAccessibility-11.0-py2.py3-none-any.whl", hash = "sha256:901961f171f7af184decbf5a3899debfa56dbd1a63a53d0ff3d93eff90f2f464", size = 4637, upload_time = "2025-01-14T18:55:08.968Z" }, + { url = "https://files.pythonhosted.org/packages/72/3f/fa350681a6599ed6756dc598fcd17fda1521249e4570a57b4a9b9c900f47/pyobjc_framework_MediaAccessibility-11.0-py3-none-any.whl", hash = "sha256:3f4b9e4d1ac8e7f8cdb7a2e9839ab75cb358dead3e6365ccd8d6017d7e93811e", size = 4708, upload_time = "2025-01-14T18:55:09.939Z" }, ] [[package]] @@ -3319,10 +3326,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coremedia" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931 } +sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931, upload_time = "2025-01-14T19:04:28.65Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/94/1e4aa67e424a043dfa886c946bb872f9653cc12ad59bd7c2c24e3d19a4f5/pyobjc_framework_MediaExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f25d674f381bae800761efe1628959293009d287f7127616f75318a87e4543d", size = 39781 }, - { url = "https://files.pythonhosted.org/packages/02/3c/2cbd4498950daadd111639a7b8dea2aaa6825526677b31ae49bc940f1036/pyobjc_framework_MediaExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9a167725f7a6921d446084b132505392bb375a5ef91498f7be5d94c0d48d26ae", size = 39777 }, + { url = "https://files.pythonhosted.org/packages/4d/94/1e4aa67e424a043dfa886c946bb872f9653cc12ad59bd7c2c24e3d19a4f5/pyobjc_framework_MediaExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f25d674f381bae800761efe1628959293009d287f7127616f75318a87e4543d", size = 39781, upload_time = "2025-01-14T18:55:13.454Z" }, + { url = "https://files.pythonhosted.org/packages/02/3c/2cbd4498950daadd111639a7b8dea2aaa6825526677b31ae49bc940f1036/pyobjc_framework_MediaExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9a167725f7a6921d446084b132505392bb375a5ef91498f7be5d94c0d48d26ae", size = 39777, upload_time = "2025-01-14T18:55:14.434Z" }, ] [[package]] @@ -3334,10 +3341,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975, upload_time = "2025-01-14T19:04:29.739Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/b6/c079b41a7a4b6b856b4ba7196500f058fb9d9f4f021269b49cf0861ace1f/pyobjc_framework_MediaLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3d273d4db7e1894fd2a95448c26eeced6e13e33555f727988aeec4b2762246fb", size = 4288 }, - { url = "https://files.pythonhosted.org/packages/b0/ae/05f2ee15f5e8524b27d6e446822edfed977c1ed0d3201644ae4d5d78bdde/pyobjc_framework_MediaLibrary-11.0-py3-none-any.whl", hash = "sha256:b8b97bb9067cf81942ce69d3273e2b18d093290c3fd692172a54f012ab64c0b3", size = 4359 }, + { url = "https://files.pythonhosted.org/packages/16/b6/c079b41a7a4b6b856b4ba7196500f058fb9d9f4f021269b49cf0861ace1f/pyobjc_framework_MediaLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3d273d4db7e1894fd2a95448c26eeced6e13e33555f727988aeec4b2762246fb", size = 4288, upload_time = "2025-01-14T18:55:20.473Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/05f2ee15f5e8524b27d6e446822edfed977c1ed0d3201644ae4d5d78bdde/pyobjc_framework_MediaLibrary-11.0-py3-none-any.whl", hash = "sha256:b8b97bb9067cf81942ce69d3273e2b18d093290c3fd692172a54f012ab64c0b3", size = 4359, upload_time = "2025-01-14T18:55:21.491Z" }, ] [[package]] @@ -3348,10 +3355,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-avfoundation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020, upload_time = "2025-01-14T19:04:30.617Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/b2/57b7b75bb5f2b624ce48cd48fb7d651d2f24d279918b352ae8fb03384b47/pyobjc_framework_MediaPlayer-11.0-py2.py3-none-any.whl", hash = "sha256:b124b0f18444b69b64142bad2579287d0b1a4a35cb6b14526523a822066d527d", size = 6903 }, - { url = "https://files.pythonhosted.org/packages/e9/8e/4969374f0fb243dd06336f2edc8c755743a683e73a57c3253279d048a455/pyobjc_framework_MediaPlayer-11.0-py3-none-any.whl", hash = "sha256:1a051624b536666feb5fd1a4bb54000ab45dac0c8aea4cd4707cbde1773acf57", size = 6977 }, + { url = "https://files.pythonhosted.org/packages/96/b2/57b7b75bb5f2b624ce48cd48fb7d651d2f24d279918b352ae8fb03384b47/pyobjc_framework_MediaPlayer-11.0-py2.py3-none-any.whl", hash = "sha256:b124b0f18444b69b64142bad2579287d0b1a4a35cb6b14526523a822066d527d", size = 6903, upload_time = "2025-01-14T18:55:24.375Z" }, + { url = "https://files.pythonhosted.org/packages/e9/8e/4969374f0fb243dd06336f2edc8c755743a683e73a57c3253279d048a455/pyobjc_framework_MediaPlayer-11.0-py3-none-any.whl", hash = "sha256:1a051624b536666feb5fd1a4bb54000ab45dac0c8aea4cd4707cbde1773acf57", size = 6977, upload_time = "2025-01-14T18:55:25.359Z" }, ] [[package]] @@ -3362,10 +3369,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067 } +sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067, upload_time = "2025-01-14T19:04:32.823Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/d5/ee184e33bd743c363d7ab59d8412289c6ac14c78a035545a067b98704ae2/pyobjc_framework_MediaToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df09e4db52d4efeafe4a324600b9c5062fd87c1d1217ebec2df65c8b6b0ce9ef", size = 12776 }, - { url = "https://files.pythonhosted.org/packages/e9/a5/c02d2c44ebcd5884d7ccf55c597c0960d14e4e8f386b65dcd76f9f50ec3d/pyobjc_framework_MediaToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e80e3057f5030fb034ac93c3e891cee346716e1669f280ebbd63ccfa52b2b7ff", size = 12937 }, + { url = "https://files.pythonhosted.org/packages/c3/d5/ee184e33bd743c363d7ab59d8412289c6ac14c78a035545a067b98704ae2/pyobjc_framework_MediaToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df09e4db52d4efeafe4a324600b9c5062fd87c1d1217ebec2df65c8b6b0ce9ef", size = 12776, upload_time = "2025-01-14T18:55:30.277Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a5/c02d2c44ebcd5884d7ccf55c597c0960d14e4e8f386b65dcd76f9f50ec3d/pyobjc_framework_MediaToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e80e3057f5030fb034ac93c3e891cee346716e1669f280ebbd63ccfa52b2b7ff", size = 12937, upload_time = "2025-01-14T18:55:34.139Z" }, ] [[package]] @@ -3376,10 +3383,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102 } +sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102, upload_time = "2025-01-14T19:04:34.011Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/fe/083727028e63ffcf7455d10288df05696737ee74a31decdc671e32624f58/pyobjc_framework_Metal-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ac5f317d52cd7523dea2e172fbe8b03e7452b907da42a0a5e5c5ab427c5e9de", size = 57321 }, - { url = "https://files.pythonhosted.org/packages/78/85/396ad46929ec6e2aa554c29a3fae2f7c7ffb2e1a3fbb9c41948d5a573dc8/pyobjc_framework_Metal-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45802d48d1a35cc66fee08539c8ca9fc6a0dc4ab700cf78a81cf5f8982ed6f5b", size = 57099 }, + { url = "https://files.pythonhosted.org/packages/e2/fe/083727028e63ffcf7455d10288df05696737ee74a31decdc671e32624f58/pyobjc_framework_Metal-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ac5f317d52cd7523dea2e172fbe8b03e7452b907da42a0a5e5c5ab427c5e9de", size = 57321, upload_time = "2025-01-14T18:55:39.025Z" }, + { url = "https://files.pythonhosted.org/packages/78/85/396ad46929ec6e2aa554c29a3fae2f7c7ffb2e1a3fbb9c41948d5a573dc8/pyobjc_framework_Metal-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45802d48d1a35cc66fee08539c8ca9fc6a0dc4ab700cf78a81cf5f8982ed6f5b", size = 57099, upload_time = "2025-01-14T18:55:40.091Z" }, ] [[package]] @@ -3390,10 +3397,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436 } +sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436, upload_time = "2025-01-14T19:04:36.161Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/f1/4140b63b3128cb2f12e136c4158a082ce170e4eb979bccb628768c59fd98/pyobjc_framework_MetalFX-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3a3847812d40cb6bb7a5f0e735f9f28cba83a1e1264d4dafc630ce894e98a20", size = 10308 }, - { url = "https://files.pythonhosted.org/packages/c0/85/460abd4f96a7a3efd36404a480ed4d31a51f4b3ed64dc4595502a5f725c3/pyobjc_framework_MetalFX-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a37dc271513b217fcba4a99c6cd92997ee171b49b974e0a9dd1b35feb32b7109", size = 10338 }, + { url = "https://files.pythonhosted.org/packages/16/f1/4140b63b3128cb2f12e136c4158a082ce170e4eb979bccb628768c59fd98/pyobjc_framework_MetalFX-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3a3847812d40cb6bb7a5f0e735f9f28cba83a1e1264d4dafc630ce894e98a20", size = 10308, upload_time = "2025-01-14T18:55:47.719Z" }, + { url = "https://files.pythonhosted.org/packages/c0/85/460abd4f96a7a3efd36404a480ed4d31a51f4b3ed64dc4595502a5f725c3/pyobjc_framework_MetalFX-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a37dc271513b217fcba4a99c6cd92997ee171b49b974e0a9dd1b35feb32b7109", size = 10338, upload_time = "2025-01-14T18:55:48.593Z" }, ] [[package]] @@ -3405,10 +3412,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628 } +sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628, upload_time = "2025-01-14T19:04:36.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/44/e7eb6746d9e1ad0ad08ab0a8ac20d264b049960363a8f28a744d1d9c319c/pyobjc_framework_MetalKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f314478a5d772d2f7b4db09957ecb63acd6e3f0cde8c18b1b6b35caa9ea7def2", size = 8598 }, - { url = "https://files.pythonhosted.org/packages/a6/1c/1ae6d629065e495e8e0b7def36e1d632e461a933f616f9776a914d69b2fd/pyobjc_framework_MetalKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f2d93180e7ac5abd906e492165a72f82d308d68101eadd213bba68a4b1dc4a8", size = 8611 }, + { url = "https://files.pythonhosted.org/packages/d3/44/e7eb6746d9e1ad0ad08ab0a8ac20d264b049960363a8f28a744d1d9c319c/pyobjc_framework_MetalKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f314478a5d772d2f7b4db09957ecb63acd6e3f0cde8c18b1b6b35caa9ea7def2", size = 8598, upload_time = "2025-01-14T18:55:56.761Z" }, + { url = "https://files.pythonhosted.org/packages/a6/1c/1ae6d629065e495e8e0b7def36e1d632e461a933f616f9776a914d69b2fd/pyobjc_framework_MetalKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f2d93180e7ac5abd906e492165a72f82d308d68101eadd213bba68a4b1dc4a8", size = 8611, upload_time = "2025-01-14T18:55:58.085Z" }, ] [[package]] @@ -3419,10 +3426,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444 } +sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444, upload_time = "2025-01-14T19:04:38.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/e9/3741ac0e745e1014961f12cf967eac1d4ec5b110d3ed13fdf9dd4ce27933/pyobjc_framework_MetalPerformanceShaders-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:460a30ff31f04bbe82bf3304949171e148e3171ba0c0773dd9bfc42dad766d2e", size = 33004 }, - { url = "https://files.pythonhosted.org/packages/39/b4/51434a9a897a47f6a0d1f6079725e3de4dbc75a7004275f116a2043cf80b/pyobjc_framework_MetalPerformanceShaders-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abd4649de32aedfa45f8535d74227ba3e1411b6426f794026e8426feab43ea8e", size = 33222 }, + { url = "https://files.pythonhosted.org/packages/e6/e9/3741ac0e745e1014961f12cf967eac1d4ec5b110d3ed13fdf9dd4ce27933/pyobjc_framework_MetalPerformanceShaders-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:460a30ff31f04bbe82bf3304949171e148e3171ba0c0773dd9bfc42dad766d2e", size = 33004, upload_time = "2025-01-14T18:56:02.993Z" }, + { url = "https://files.pythonhosted.org/packages/39/b4/51434a9a897a47f6a0d1f6079725e3de4dbc75a7004275f116a2043cf80b/pyobjc_framework_MetalPerformanceShaders-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abd4649de32aedfa45f8535d74227ba3e1411b6426f794026e8426feab43ea8e", size = 33222, upload_time = "2025-01-14T18:56:04.78Z" }, ] [[package]] @@ -3433,10 +3440,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-metalperformanceshaders" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381, upload_time = "2025-01-14T19:04:39.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/8c/3d8f1cc6cfe7f9fd73f3911bb62256fdefc4d7f5375b8be84870d8c15650/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py2.py3-none-any.whl", hash = "sha256:d48ffe401fbc8273a23e908685635a51c64d4ebfb5ad32742664ab9fac6c5194", size = 6403 }, - { url = "https://files.pythonhosted.org/packages/ef/26/ca0441ac11d5ecc7814b48b3af9df467ead93622f0edc67e947f1a4afe97/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py3-none-any.whl", hash = "sha256:f0702a6e91b273e552283ff2782220ce08eb65325aa45ad428e0b7f3b45cf211", size = 6474 }, + { url = "https://files.pythonhosted.org/packages/0d/8c/3d8f1cc6cfe7f9fd73f3911bb62256fdefc4d7f5375b8be84870d8c15650/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py2.py3-none-any.whl", hash = "sha256:d48ffe401fbc8273a23e908685635a51c64d4ebfb5ad32742664ab9fac6c5194", size = 6403, upload_time = "2025-01-14T18:56:10.236Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/ca0441ac11d5ecc7814b48b3af9df467ead93622f0edc67e947f1a4afe97/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py3-none-any.whl", hash = "sha256:f0702a6e91b273e552283ff2782220ce08eb65325aa45ad428e0b7f3b45cf211", size = 6474, upload_time = "2025-01-14T18:56:11.479Z" }, ] [[package]] @@ -3447,10 +3454,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414 } +sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414, upload_time = "2025-01-14T19:04:41.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/1f/cc897b07b3ed96a26a3008f43e0deefaa60e280ac13118a2ff4224fca0d8/pyobjc_framework_MetricKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:422b6ca1f082dae864df8cc4aa0bac3829be95675b72ef63cd3ee00d30966b97", size = 7958 }, - { url = "https://files.pythonhosted.org/packages/19/63/f37010479670958d3c976d007d45107c3fc53b5626586527c6310821e15a/pyobjc_framework_MetricKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b94313601bbf0181c8f75712e82646261ff0e020da5c83d25914952db53a7955", size = 7966 }, + { url = "https://files.pythonhosted.org/packages/13/1f/cc897b07b3ed96a26a3008f43e0deefaa60e280ac13118a2ff4224fca0d8/pyobjc_framework_MetricKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:422b6ca1f082dae864df8cc4aa0bac3829be95675b72ef63cd3ee00d30966b97", size = 7958, upload_time = "2025-01-14T18:56:13.428Z" }, + { url = "https://files.pythonhosted.org/packages/19/63/f37010479670958d3c976d007d45107c3fc53b5626586527c6310821e15a/pyobjc_framework_MetricKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b94313601bbf0181c8f75712e82646261ff0e020da5c83d25914952db53a7955", size = 7966, upload_time = "2025-01-14T18:56:14.36Z" }, ] [[package]] @@ -3461,10 +3468,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265 } +sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265, upload_time = "2025-01-14T19:04:43.326Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/06/a5865c0e4db4e7289bf6b40242b7149af87d5779f34ca168df5cabf2d5a4/pyobjc_framework_MLCompute-11.0-py2.py3-none-any.whl", hash = "sha256:16ec2942af9915f931df76b42e7f42348109b599faef955f5bea540735f87677", size = 6729 }, - { url = "https://files.pythonhosted.org/packages/b5/15/3c69df5b5b99cea4a573e1d0e3c0b607cfe4ea1404ea1fe3a302361eb452/pyobjc_framework_MLCompute-11.0-py3-none-any.whl", hash = "sha256:bcdf94fe060fb034aed41db84af1cfcdbf3925e69b2b11df89d4546fac6cf0bf", size = 6799 }, + { url = "https://files.pythonhosted.org/packages/75/06/a5865c0e4db4e7289bf6b40242b7149af87d5779f34ca168df5cabf2d5a4/pyobjc_framework_MLCompute-11.0-py2.py3-none-any.whl", hash = "sha256:16ec2942af9915f931df76b42e7f42348109b599faef955f5bea540735f87677", size = 6729, upload_time = "2025-01-14T18:54:55.927Z" }, + { url = "https://files.pythonhosted.org/packages/b5/15/3c69df5b5b99cea4a573e1d0e3c0b607cfe4ea1404ea1fe3a302361eb452/pyobjc_framework_MLCompute-11.0-py3-none-any.whl", hash = "sha256:bcdf94fe060fb034aed41db84af1cfcdbf3925e69b2b11df89d4546fac6cf0bf", size = 6799, upload_time = "2025-01-14T18:54:56.893Z" }, ] [[package]] @@ -3476,10 +3483,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652, upload_time = "2025-01-14T19:04:44.263Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/98/a30e8df5624c7929dfcd9748bf859929e8aa2c7d836efe5888dafc05f729/pyobjc_framework_ModelIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c126318b878ffb31c39b0c7c91ca20a3b46c14c18f000e3bfb854e4541fe0147", size = 20715 }, - { url = "https://files.pythonhosted.org/packages/a9/f8/bb4bc635eb16331c20731cae2e495645d0d10e25962451631eb9085a3f85/pyobjc_framework_ModelIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a7357f07b77f3ab0a8107d827acdbc3e1fd458ce396335c057930b6a3f225a93", size = 20715 }, + { url = "https://files.pythonhosted.org/packages/11/98/a30e8df5624c7929dfcd9748bf859929e8aa2c7d836efe5888dafc05f729/pyobjc_framework_ModelIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c126318b878ffb31c39b0c7c91ca20a3b46c14c18f000e3bfb854e4541fe0147", size = 20715, upload_time = "2025-01-14T18:56:22.163Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f8/bb4bc635eb16331c20731cae2e495645d0d10e25962451631eb9085a3f85/pyobjc_framework_ModelIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a7357f07b77f3ab0a8107d827acdbc3e1fd458ce396335c057930b6a3f225a93", size = 20715, upload_time = "2025-01-14T18:56:23.297Z" }, ] [[package]] @@ -3490,10 +3497,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534 } +sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534, upload_time = "2025-01-14T19:04:45.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/d2/a4144f966cbe998f8da46b936783561bcd3e7e84b8f2dc45eb49ee3f6f21/pyobjc_framework_MultipeerConnectivity-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e338b22f5b0fcb398e316552398c252bedfc3375c058340861eb205e3cdf994e", size = 12423 }, - { url = "https://files.pythonhosted.org/packages/7b/50/ac9213aca34d30993a36525c23d19ba5a568d3ea4e31e3bc2a6940ddafde/pyobjc_framework_MultipeerConnectivity-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66bef15f5e5afd6b966cdadf2162082b0171f4a45af6d2cb2644f38431011911", size = 12447 }, + { url = "https://files.pythonhosted.org/packages/2d/d2/a4144f966cbe998f8da46b936783561bcd3e7e84b8f2dc45eb49ee3f6f21/pyobjc_framework_MultipeerConnectivity-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e338b22f5b0fcb398e316552398c252bedfc3375c058340861eb205e3cdf994e", size = 12423, upload_time = "2025-01-14T18:56:30.132Z" }, + { url = "https://files.pythonhosted.org/packages/7b/50/ac9213aca34d30993a36525c23d19ba5a568d3ea4e31e3bc2a6940ddafde/pyobjc_framework_MultipeerConnectivity-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66bef15f5e5afd6b966cdadf2162082b0171f4a45af6d2cb2644f38431011911", size = 12447, upload_time = "2025-01-14T18:56:31.04Z" }, ] [[package]] @@ -3504,10 +3511,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385 } +sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385, upload_time = "2025-01-14T19:04:46.185Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/72/2246c0a6dc2d087951a626157f52c81cf88fe28393994163e9572fd1eb61/pyobjc_framework_NaturalLanguage-11.0-py2.py3-none-any.whl", hash = "sha256:0744a2871690dcc9ec9e7169023b492abdde63ef97abde46013c01477b4d047c", size = 5250 }, - { url = "https://files.pythonhosted.org/packages/3a/49/f5faf3fab0f1ffb21882115878f1e5023257239aa576d6c01c31e42dd1da/pyobjc_framework_NaturalLanguage-11.0-py3-none-any.whl", hash = "sha256:7c021b270fda5469b56b9804e860cf5a80a485b817fc5fd3bb002383b2982d94", size = 5321 }, + { url = "https://files.pythonhosted.org/packages/7d/72/2246c0a6dc2d087951a626157f52c81cf88fe28393994163e9572fd1eb61/pyobjc_framework_NaturalLanguage-11.0-py2.py3-none-any.whl", hash = "sha256:0744a2871690dcc9ec9e7169023b492abdde63ef97abde46013c01477b4d047c", size = 5250, upload_time = "2025-01-14T18:56:34.675Z" }, + { url = "https://files.pythonhosted.org/packages/3a/49/f5faf3fab0f1ffb21882115878f1e5023257239aa576d6c01c31e42dd1da/pyobjc_framework_NaturalLanguage-11.0-py3-none-any.whl", hash = "sha256:7c021b270fda5469b56b9804e860cf5a80a485b817fc5fd3bb002383b2982d94", size = 5321, upload_time = "2025-01-14T18:56:36.377Z" }, ] [[package]] @@ -3518,10 +3525,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173, upload_time = "2025-01-14T19:04:47.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/e7/4be35bc2adbebffb5ac7ede2b8459432194a82bd8f325af12b77b7c26248/pyobjc_framework_NetFS-11.0-py2.py3-none-any.whl", hash = "sha256:11e06da73a1d590b8462f3a1412604758d49b5e04d134b6e991282453b76abb8", size = 4088 }, - { url = "https://files.pythonhosted.org/packages/fe/83/b7c8dfaee82c0312af25c2b31621505ce19f01fab7bb55eec69c0b4d24ad/pyobjc_framework_NetFS-11.0-py3-none-any.whl", hash = "sha256:9b69a36e3a6782ce37cd3140c584dd7d5c96f7355662d004a2927583b112b4dd", size = 4162 }, + { url = "https://files.pythonhosted.org/packages/00/e7/4be35bc2adbebffb5ac7ede2b8459432194a82bd8f325af12b77b7c26248/pyobjc_framework_NetFS-11.0-py2.py3-none-any.whl", hash = "sha256:11e06da73a1d590b8462f3a1412604758d49b5e04d134b6e991282453b76abb8", size = 4088, upload_time = "2025-01-14T18:56:37.646Z" }, + { url = "https://files.pythonhosted.org/packages/fe/83/b7c8dfaee82c0312af25c2b31621505ce19f01fab7bb55eec69c0b4d24ad/pyobjc_framework_NetFS-11.0-py3-none-any.whl", hash = "sha256:9b69a36e3a6782ce37cd3140c584dd7d5c96f7355662d004a2927583b112b4dd", size = 4162, upload_time = "2025-01-14T18:56:38.682Z" }, ] [[package]] @@ -3532,10 +3539,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160 } +sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160, upload_time = "2025-01-14T19:04:50.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/b5/16800524e6d8d99467f53dbafa661abb1405d08d50def7edb933504197a3/pyobjc_framework_Network-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6fc797537690a241b555475923bcee28824efacd501e235457daeb4496b4b700", size = 19507 }, - { url = "https://files.pythonhosted.org/packages/36/7c/a5966976564e8e71c0e66bf68e9282c279ad0c3ce81be61fa20ca8e0ca2e/pyobjc_framework_Network-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b9bb4a0cbd01cc4acb120ce313662763bca0c5ef11c01a0a0cae64c80b120c5", size = 19532 }, + { url = "https://files.pythonhosted.org/packages/24/b5/16800524e6d8d99467f53dbafa661abb1405d08d50def7edb933504197a3/pyobjc_framework_Network-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6fc797537690a241b555475923bcee28824efacd501e235457daeb4496b4b700", size = 19507, upload_time = "2025-01-14T18:56:41.544Z" }, + { url = "https://files.pythonhosted.org/packages/36/7c/a5966976564e8e71c0e66bf68e9282c279ad0c3ce81be61fa20ca8e0ca2e/pyobjc_framework_Network-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b9bb4a0cbd01cc4acb120ce313662763bca0c5ef11c01a0a0cae64c80b120c5", size = 19532, upload_time = "2025-01-14T18:56:42.499Z" }, ] [[package]] @@ -3546,10 +3553,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551 } +sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551, upload_time = "2025-01-14T19:04:51.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/a4/120aba6e1ccf473d7294c200687f500b096947fec58d94dc772b1a444ecc/pyobjc_framework_NetworkExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bba4f338748c8ad2cb4320c4dd64b64772a863c6b6f991c2636b2a2f4cb839a", size = 13945 }, - { url = "https://files.pythonhosted.org/packages/d1/0f/f7039d2bae0dcd63f66aff008613860499b6014dbd272726026f6c4c768d/pyobjc_framework_NetworkExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abf63433992ff1830f42cb813d1575473f0034ca6f62827f43bb2b33cc31e095", size = 13960 }, + { url = "https://files.pythonhosted.org/packages/f2/a4/120aba6e1ccf473d7294c200687f500b096947fec58d94dc772b1a444ecc/pyobjc_framework_NetworkExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bba4f338748c8ad2cb4320c4dd64b64772a863c6b6f991c2636b2a2f4cb839a", size = 13945, upload_time = "2025-01-14T18:56:53.477Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0f/f7039d2bae0dcd63f66aff008613860499b6014dbd272726026f6c4c768d/pyobjc_framework_NetworkExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abf63433992ff1830f42cb813d1575473f0034ca6f62827f43bb2b33cc31e095", size = 13960, upload_time = "2025-01-14T18:56:55.661Z" }, ] [[package]] @@ -3560,10 +3567,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844 } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844, upload_time = "2025-01-14T19:04:52.459Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/f2/22f04062b772e2f47ee2d54eac3f80c5aef727ec468ef5ab9a3272dd2a73/pyobjc_framework_NotificationCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:075853f3e36eb4377182589e552226b2207a575035d7e128055cfde9dcad84b7", size = 9684 }, - { url = "https://files.pythonhosted.org/packages/16/22/531c2aab1639ab13aeaf3ac324afa102515b8d5eb860cb1a566018d98058/pyobjc_framework_NotificationCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:093e50badfbc78edf088f9241cddba7516a58188d401f299e361f1ec85e93fce", size = 9707 }, + { url = "https://files.pythonhosted.org/packages/bf/f2/22f04062b772e2f47ee2d54eac3f80c5aef727ec468ef5ab9a3272dd2a73/pyobjc_framework_NotificationCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:075853f3e36eb4377182589e552226b2207a575035d7e128055cfde9dcad84b7", size = 9684, upload_time = "2025-01-14T18:57:02.779Z" }, + { url = "https://files.pythonhosted.org/packages/16/22/531c2aab1639ab13aeaf3ac324afa102515b8d5eb860cb1a566018d98058/pyobjc_framework_NotificationCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:093e50badfbc78edf088f9241cddba7516a58188d401f299e361f1ec85e93fce", size = 9707, upload_time = "2025-01-14T18:57:03.659Z" }, ] [[package]] @@ -3574,10 +3581,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165 } +sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165, upload_time = "2025-01-14T19:04:53.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/0a/e5a03c46a5873db83fb89ea829e4a0c02fb3f56f3639a6053e72854f435b/pyobjc_framework_OpenDirectory-11.0-py2.py3-none-any.whl", hash = "sha256:8a0feeda5a7f34b25b72c71cd1e4dd57b636cc4103248ff91bcb8571d4915eb4", size = 11747 }, - { url = "https://files.pythonhosted.org/packages/da/fd/be3815a19978ab2a3abe9563a031195b40647077fcebbee86232af260176/pyobjc_framework_OpenDirectory-11.0-py3-none-any.whl", hash = "sha256:bfac495de433a62e3934619e2f5d2254177f960b7d4e905ed4ef359127e23b24", size = 11816 }, + { url = "https://files.pythonhosted.org/packages/b4/0a/e5a03c46a5873db83fb89ea829e4a0c02fb3f56f3639a6053e72854f435b/pyobjc_framework_OpenDirectory-11.0-py2.py3-none-any.whl", hash = "sha256:8a0feeda5a7f34b25b72c71cd1e4dd57b636cc4103248ff91bcb8571d4915eb4", size = 11747, upload_time = "2025-01-14T18:57:17.445Z" }, + { url = "https://files.pythonhosted.org/packages/da/fd/be3815a19978ab2a3abe9563a031195b40647077fcebbee86232af260176/pyobjc_framework_OpenDirectory-11.0-py3-none-any.whl", hash = "sha256:bfac495de433a62e3934619e2f5d2254177f960b7d4e905ed4ef359127e23b24", size = 11816, upload_time = "2025-01-14T18:57:18.486Z" }, ] [[package]] @@ -3588,10 +3595,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535, upload_time = "2025-01-14T19:04:54.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/f6/1dcff2f76280946368ee75ab39c92e261a851656c5979a50513563d08cf0/pyobjc_framework_OSAKit-11.0-py2.py3-none-any.whl", hash = "sha256:3183414e345af83a2187b00356130909a7c2a41b2227dc579b662737300c3ba4", size = 4094 }, - { url = "https://files.pythonhosted.org/packages/17/75/745985429f0ff4776ffb8ba261199e11f4d6977b1814ad2b39084f83bad5/pyobjc_framework_OSAKit-11.0-py3-none-any.whl", hash = "sha256:79150c47d2aeffc72fb6551060518ce472275edbad3b56aef5923a6086371c28", size = 4162 }, + { url = "https://files.pythonhosted.org/packages/56/f6/1dcff2f76280946368ee75ab39c92e261a851656c5979a50513563d08cf0/pyobjc_framework_OSAKit-11.0-py2.py3-none-any.whl", hash = "sha256:3183414e345af83a2187b00356130909a7c2a41b2227dc579b662737300c3ba4", size = 4094, upload_time = "2025-01-14T18:57:08.639Z" }, + { url = "https://files.pythonhosted.org/packages/17/75/745985429f0ff4776ffb8ba261199e11f4d6977b1814ad2b39084f83bad5/pyobjc_framework_OSAKit-11.0-py3-none-any.whl", hash = "sha256:79150c47d2aeffc72fb6551060518ce472275edbad3b56aef5923a6086371c28", size = 4162, upload_time = "2025-01-14T18:57:09.71Z" }, ] [[package]] @@ -3604,10 +3611,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151 } +sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151, upload_time = "2025-01-14T19:04:55.587Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/54/6b507a18d0adadf8b707be9616bc9bab157963b81fa3c9928a0148d3bfd8/pyobjc_framework_OSLog-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0131851fca9b741f290ffa727dd30328dd8526b87c8cef623b79239bed99187", size = 7694 }, - { url = "https://files.pythonhosted.org/packages/d1/79/81e64a55023f458aa5d99d10671fd9bcc6c0dcf8339768152fbc28c92cef/pyobjc_framework_OSLog-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:17d8b49113a476372b24ac8e544d88f6d12f878f1081dd611ab203c4484f2039", size = 7720 }, + { url = "https://files.pythonhosted.org/packages/9c/54/6b507a18d0adadf8b707be9616bc9bab157963b81fa3c9928a0148d3bfd8/pyobjc_framework_OSLog-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0131851fca9b741f290ffa727dd30328dd8526b87c8cef623b79239bed99187", size = 7694, upload_time = "2025-01-14T18:57:12.713Z" }, + { url = "https://files.pythonhosted.org/packages/d1/79/81e64a55023f458aa5d99d10671fd9bcc6c0dcf8339768152fbc28c92cef/pyobjc_framework_OSLog-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:17d8b49113a476372b24ac8e544d88f6d12f878f1081dd611ab203c4484f2039", size = 7720, upload_time = "2025-01-14T18:57:13.695Z" }, ] [[package]] @@ -3618,10 +3625,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495 } +sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495, upload_time = "2025-01-14T19:04:57.689Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/72/d7dae8f5a1c5b12d9cf404a71a82fd5a638bc4de2d1099bf838aee1026f0/pyobjc_framework_PassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:710372134c3adedb9017bfc2fbc592ef0e94ae916145b58e57234239bf903b90", size = 14354 }, - { url = "https://files.pythonhosted.org/packages/c3/b1/5ee2f5581877241a4fc2db4ab4a33d595a918bde1b4a59796240e2b2244b/pyobjc_framework_PassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe0144177f7feb96577bea53841d9b9b3f63185735a1bf1b36368ab189fd6282", size = 14391 }, + { url = "https://files.pythonhosted.org/packages/53/72/d7dae8f5a1c5b12d9cf404a71a82fd5a638bc4de2d1099bf838aee1026f0/pyobjc_framework_PassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:710372134c3adedb9017bfc2fbc592ef0e94ae916145b58e57234239bf903b90", size = 14354, upload_time = "2025-01-14T18:57:24.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b1/5ee2f5581877241a4fc2db4ab4a33d595a918bde1b4a59796240e2b2244b/pyobjc_framework_PassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe0144177f7feb96577bea53841d9b9b3f63185735a1bf1b36368ab189fd6282", size = 14391, upload_time = "2025-01-14T18:57:26.67Z" }, ] [[package]] @@ -3632,10 +3639,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812 } +sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812, upload_time = "2025-01-14T19:04:58.652Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/5b/24fb83a97648eaa0d231df7908532dff7b36d5f516d55c92ed9ae07c4e1b/pyobjc_framework_PencilKit-11.0-py2.py3-none-any.whl", hash = "sha256:22cbb6ed2504be4c8d631c4711b00fae48ef731c10c69861b4de1e4fcdc19279", size = 3970 }, - { url = "https://files.pythonhosted.org/packages/08/fd/89a005c86b06137837952838d976ce6e39b31082392d78c382d44e03944d/pyobjc_framework_PencilKit-11.0-py3-none-any.whl", hash = "sha256:a4e606c5b69e6adb80ef30fc95fe0095971735d12ab6fc4fe4d982e4c8a3881a", size = 4045 }, + { url = "https://files.pythonhosted.org/packages/af/5b/24fb83a97648eaa0d231df7908532dff7b36d5f516d55c92ed9ae07c4e1b/pyobjc_framework_PencilKit-11.0-py2.py3-none-any.whl", hash = "sha256:22cbb6ed2504be4c8d631c4711b00fae48ef731c10c69861b4de1e4fcdc19279", size = 3970, upload_time = "2025-01-14T18:57:30.597Z" }, + { url = "https://files.pythonhosted.org/packages/08/fd/89a005c86b06137837952838d976ce6e39b31082392d78c382d44e03944d/pyobjc_framework_PencilKit-11.0-py3-none-any.whl", hash = "sha256:a4e606c5b69e6adb80ef30fc95fe0095971735d12ab6fc4fe4d982e4c8a3881a", size = 4045, upload_time = "2025-01-14T18:57:31.87Z" }, ] [[package]] @@ -3646,10 +3653,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-avfoundation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214 } +sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214, upload_time = "2025-01-14T19:04:59.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/97/efb9d770ba05d285384b0c121e9e911929893356da1944a0bb03ea0df0f2/pyobjc_framework_PHASE-11.0-py2.py3-none-any.whl", hash = "sha256:d3e41c2b2fdf4b2ce39f558a08762c6864449ff87b618e42747777ad3f821323", size = 6777 }, - { url = "https://files.pythonhosted.org/packages/38/85/03420927e4243d0ef8e3e8aa1ca511b5638743d7ec319a570a472a50d60f/pyobjc_framework_PHASE-11.0-py3-none-any.whl", hash = "sha256:78c0600477ea294304b51f8284a2fb299be284c33ae2c135e1c7cd26fdf4def4", size = 6846 }, + { url = "https://files.pythonhosted.org/packages/a9/97/efb9d770ba05d285384b0c121e9e911929893356da1944a0bb03ea0df0f2/pyobjc_framework_PHASE-11.0-py2.py3-none-any.whl", hash = "sha256:d3e41c2b2fdf4b2ce39f558a08762c6864449ff87b618e42747777ad3f821323", size = 6777, upload_time = "2025-01-14T18:57:20.135Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/03420927e4243d0ef8e3e8aa1ca511b5638743d7ec319a570a472a50d60f/pyobjc_framework_PHASE-11.0-py3-none-any.whl", hash = "sha256:78c0600477ea294304b51f8284a2fb299be284c33ae2c135e1c7cd26fdf4def4", size = 6846, upload_time = "2025-01-14T18:57:21.193Z" }, ] [[package]] @@ -3660,10 +3667,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122 } +sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122, upload_time = "2025-01-14T19:05:01.804Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/27/62e5833b9629121b4b6ea8f2b2aa295cf6b719dc6316387f77ec0bd41d77/pyobjc_framework_Photos-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:71bf849888713e4a00eb44074c5000ed081c905ba35b3a55ee84c6367ce60ce8", size = 12085 }, - { url = "https://files.pythonhosted.org/packages/b9/6e/54108271ea34b0fc51bf8d0bf677788e4d39a1e29ad481f8c78c100f3159/pyobjc_framework_Photos-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ea630c3abf4620b022f23167ef5f3d6b157b38697d7ffc5df0fc507e95bed655", size = 12107 }, + { url = "https://files.pythonhosted.org/packages/80/27/62e5833b9629121b4b6ea8f2b2aa295cf6b719dc6316387f77ec0bd41d77/pyobjc_framework_Photos-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:71bf849888713e4a00eb44074c5000ed081c905ba35b3a55ee84c6367ce60ce8", size = 12085, upload_time = "2025-01-14T18:57:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/b9/6e/54108271ea34b0fc51bf8d0bf677788e4d39a1e29ad481f8c78c100f3159/pyobjc_framework_Photos-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ea630c3abf4620b022f23167ef5f3d6b157b38697d7ffc5df0fc507e95bed655", size = 12107, upload_time = "2025-01-14T18:57:35.66Z" }, ] [[package]] @@ -3674,10 +3681,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898 } +sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898, upload_time = "2025-01-14T19:05:02.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/ec/9574692e2852d546b28bac853b2b0584c4d4f093a4befac0e105789ee9f6/pyobjc_framework_PhotosUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b3865d2cc4fad4d34255941fe93ce504b9d2c7a7043bd0f4c715da9f4af1cf1", size = 12165 }, - { url = "https://files.pythonhosted.org/packages/90/a9/85d70fe9eee0d15a0615a3f7b2ef92120c32614e350286d347d733fcf1d0/pyobjc_framework_PhotosUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66826184121cd15415750d801160721adad80b53cbb315192522229b17252ebb", size = 12176 }, + { url = "https://files.pythonhosted.org/packages/94/ec/9574692e2852d546b28bac853b2b0584c4d4f093a4befac0e105789ee9f6/pyobjc_framework_PhotosUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b3865d2cc4fad4d34255941fe93ce504b9d2c7a7043bd0f4c715da9f4af1cf1", size = 12165, upload_time = "2025-01-14T18:57:44.048Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/85d70fe9eee0d15a0615a3f7b2ef92120c32614e350286d347d733fcf1d0/pyobjc_framework_PhotosUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66826184121cd15415750d801160721adad80b53cbb315192522229b17252ebb", size = 12176, upload_time = "2025-01-14T18:57:44.993Z" }, ] [[package]] @@ -3688,10 +3695,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419 } +sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419, upload_time = "2025-01-14T19:05:03.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/f7/5d0d9b94563ef06fe0a9c15ba2b77922b73bcc4b6630c487936edf382e20/pyobjc_framework_PreferencePanes-11.0-py2.py3-none-any.whl", hash = "sha256:2143851549430d6bb951adae44cb65c1986662ac7c8cbe15891ed194cbe283a2", size = 4706 }, - { url = "https://files.pythonhosted.org/packages/9b/0e/76d694eea953b39318249ae24c956c3e115d8222343fb01f0186f7ca0043/pyobjc_framework_PreferencePanes-11.0-py3-none-any.whl", hash = "sha256:9f1287716374338fa99445ca13dfcc6c9be5597c8a5ce06680a8ca245b4e0acc", size = 4772 }, + { url = "https://files.pythonhosted.org/packages/70/f7/5d0d9b94563ef06fe0a9c15ba2b77922b73bcc4b6630c487936edf382e20/pyobjc_framework_PreferencePanes-11.0-py2.py3-none-any.whl", hash = "sha256:2143851549430d6bb951adae44cb65c1986662ac7c8cbe15891ed194cbe283a2", size = 4706, upload_time = "2025-01-14T18:57:51.425Z" }, + { url = "https://files.pythonhosted.org/packages/9b/0e/76d694eea953b39318249ae24c956c3e115d8222343fb01f0186f7ca0043/pyobjc_framework_PreferencePanes-11.0-py3-none-any.whl", hash = "sha256:9f1287716374338fa99445ca13dfcc6c9be5597c8a5ce06680a8ca245b4e0acc", size = 4772, upload_time = "2025-01-14T18:57:52.684Z" }, ] [[package]] @@ -3702,10 +3709,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816 } +sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816, upload_time = "2025-01-14T19:05:05.468Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/5f/de178da22fa628cd88f599fea2a70b7d1d9ebc65576307df0bf29822a347/pyobjc_framework_PushKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0185cebcc5aad73aae50804c7a2412da6275717b8f872b830d71c484efcdea7a", size = 8010 }, - { url = "https://files.pythonhosted.org/packages/5f/a5/60f93031302aba7cdff28728b8141b58c3bd5c12f4a6cef5796a8cc2e666/pyobjc_framework_PushKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:43bd1ed31664982e4d8397a7e07e58a7deb85bf9c9866ea966fd7ca25796014c", size = 8032 }, + { url = "https://files.pythonhosted.org/packages/17/5f/de178da22fa628cd88f599fea2a70b7d1d9ebc65576307df0bf29822a347/pyobjc_framework_PushKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0185cebcc5aad73aae50804c7a2412da6275717b8f872b830d71c484efcdea7a", size = 8010, upload_time = "2025-01-14T18:57:59.042Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a5/60f93031302aba7cdff28728b8141b58c3bd5c12f4a6cef5796a8cc2e666/pyobjc_framework_PushKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:43bd1ed31664982e4d8397a7e07e58a7deb85bf9c9866ea966fd7ca25796014c", size = 8032, upload_time = "2025-01-14T18:57:59.973Z" }, ] [[package]] @@ -3716,10 +3723,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463 } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463, upload_time = "2025-01-14T19:05:07.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/6a/68957c8c5e8f0128d4d419728bac397d48fa7ad7a66e82b70e64d129ffca/pyobjc_framework_Quartz-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d251696bfd8e8ef72fbc90eb29fec95cb9d1cc409008a183d5cc3246130ae8c2", size = 212349 }, - { url = "https://files.pythonhosted.org/packages/60/5d/df827b78dcb5140652ad08af8038c9ddd7e01e6bdf84462bfee644e6e661/pyobjc_framework_Quartz-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cb4a9f2d9d580ea15e25e6b270f47681afb5689cafc9e25712445ce715bcd18e", size = 212061 }, + { url = "https://files.pythonhosted.org/packages/a3/6a/68957c8c5e8f0128d4d419728bac397d48fa7ad7a66e82b70e64d129ffca/pyobjc_framework_Quartz-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d251696bfd8e8ef72fbc90eb29fec95cb9d1cc409008a183d5cc3246130ae8c2", size = 212349, upload_time = "2025-01-14T18:58:08.963Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/df827b78dcb5140652ad08af8038c9ddd7e01e6bdf84462bfee644e6e661/pyobjc_framework_Quartz-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cb4a9f2d9d580ea15e25e6b270f47681afb5689cafc9e25712445ce715bcd18e", size = 212061, upload_time = "2025-01-14T18:58:10.2Z" }, ] [[package]] @@ -3731,10 +3738,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784 } +sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784, upload_time = "2025-01-14T19:05:09.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/85/1a66fefa99e7a4eb7534b2f56f9a24d33beda450dd2ca45d180307e76c74/pyobjc_framework_QuickLookThumbnailing-11.0-py2.py3-none-any.whl", hash = "sha256:6e567a764942845ce4db7ccfc0f8a9d091216bd029ecca955e618a43d64a5d84", size = 4164 }, - { url = "https://files.pythonhosted.org/packages/05/d7/26decb13136b7c95a1ca3ecf202644ad2fd515a57e1117c71bfc86429b20/pyobjc_framework_QuickLookThumbnailing-11.0-py3-none-any.whl", hash = "sha256:e0f7f62b9a1df55e5f717518baf3260dc2cb8a9722cc5e9c6fffc643f69bda27", size = 4229 }, + { url = "https://files.pythonhosted.org/packages/9d/85/1a66fefa99e7a4eb7534b2f56f9a24d33beda450dd2ca45d180307e76c74/pyobjc_framework_QuickLookThumbnailing-11.0-py2.py3-none-any.whl", hash = "sha256:6e567a764942845ce4db7ccfc0f8a9d091216bd029ecca955e618a43d64a5d84", size = 4164, upload_time = "2025-01-14T18:58:16.381Z" }, + { url = "https://files.pythonhosted.org/packages/05/d7/26decb13136b7c95a1ca3ecf202644ad2fd515a57e1117c71bfc86429b20/pyobjc_framework_QuickLookThumbnailing-11.0-py3-none-any.whl", hash = "sha256:e0f7f62b9a1df55e5f717518baf3260dc2cb8a9722cc5e9c6fffc643f69bda27", size = 4229, upload_time = "2025-01-14T18:58:17.404Z" }, ] [[package]] @@ -3745,10 +3752,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589 } +sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589, upload_time = "2025-01-14T19:05:10.791Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/56/89a8544426a46bf176c9462511c08d4c94ae7e0403abb2d73632af68ee8e/pyobjc_framework_ReplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:262fb834400e8379f4c795e65137763348992f3010284602d876050b8adb9ea4", size = 9904 }, - { url = "https://files.pythonhosted.org/packages/47/af/9abfa41060efc96000cc9ae77f302bb8210f3be0f793ba5d11f98a03e468/pyobjc_framework_ReplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9db123ee52761a670c6e41e5f9d9a47a2ca5582a9c4a7c8662a8bb56a0f593", size = 9903 }, + { url = "https://files.pythonhosted.org/packages/fe/56/89a8544426a46bf176c9462511c08d4c94ae7e0403abb2d73632af68ee8e/pyobjc_framework_ReplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:262fb834400e8379f4c795e65137763348992f3010284602d876050b8adb9ea4", size = 9904, upload_time = "2025-01-14T18:58:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/47/af/9abfa41060efc96000cc9ae77f302bb8210f3be0f793ba5d11f98a03e468/pyobjc_framework_ReplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9db123ee52761a670c6e41e5f9d9a47a2ca5582a9c4a7c8662a8bb56a0f593", size = 9903, upload_time = "2025-01-14T18:58:20.222Z" }, ] [[package]] @@ -3759,10 +3766,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367 } +sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367, upload_time = "2025-01-14T19:05:12.914Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/39/d69f8e7dbf6f366cb5fdaa8aa7ceef1dadb93a5e4d9fc63217477bba5e32/pyobjc_framework_SafariServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55c02a533073e0a2aaf6db544f087fd861bace6b62035c3bb2e6b20f0b921b2b", size = 7262 }, - { url = "https://files.pythonhosted.org/packages/36/76/a625330bdf7a5d9962299562b6e19f6cbd1ea1b14887958e42a4372d3344/pyobjc_framework_SafariServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31ba086a39ee06d8622a504e3ea3a1f6dc8fab1d4c4c7930d5af6e989f38ec56", size = 7262 }, + { url = "https://files.pythonhosted.org/packages/40/39/d69f8e7dbf6f366cb5fdaa8aa7ceef1dadb93a5e4d9fc63217477bba5e32/pyobjc_framework_SafariServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55c02a533073e0a2aaf6db544f087fd861bace6b62035c3bb2e6b20f0b921b2b", size = 7262, upload_time = "2025-01-14T18:58:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/36/76/a625330bdf7a5d9962299562b6e19f6cbd1ea1b14887958e42a4372d3344/pyobjc_framework_SafariServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31ba086a39ee06d8622a504e3ea3a1f6dc8fab1d4c4c7930d5af6e989f38ec56", size = 7262, upload_time = "2025-01-14T18:58:30.725Z" }, ] [[package]] @@ -3774,10 +3781,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745, upload_time = "2025-01-14T19:05:13.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/c5/68b79c0f128eb735397aa68a40e5ac48b88c12967f69358f25f753a3fc1c/pyobjc_framework_SafetyKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83a1f313c9c63ba107a7c543a8300ae225fa5ff17d963b1c499859da45ceaf55", size = 8395 }, - { url = "https://files.pythonhosted.org/packages/99/02/2853a00e75cca8db8b5053ff2648ff2a26f5c02f07af1c70630a36b58d04/pyobjc_framework_SafetyKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c6dd23fcaca9c41d6aadf2ca0a6d07c4032a0c4ea8873ee06da6efd1e868f97e", size = 8418 }, + { url = "https://files.pythonhosted.org/packages/37/c5/68b79c0f128eb735397aa68a40e5ac48b88c12967f69358f25f753a3fc1c/pyobjc_framework_SafetyKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83a1f313c9c63ba107a7c543a8300ae225fa5ff17d963b1c499859da45ceaf55", size = 8395, upload_time = "2025-01-14T18:58:35.46Z" }, + { url = "https://files.pythonhosted.org/packages/99/02/2853a00e75cca8db8b5053ff2648ff2a26f5c02f07af1c70630a36b58d04/pyobjc_framework_SafetyKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c6dd23fcaca9c41d6aadf2ca0a6d07c4032a0c4ea8873ee06da6efd1e868f97e", size = 8418, upload_time = "2025-01-14T18:58:36.369Z" }, ] [[package]] @@ -3789,10 +3796,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647 } +sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647, upload_time = "2025-01-14T19:05:15.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/4c/5ec624ae043fbbe15be2a989e3fc6cb08d992e0a5061450b84b33f96429c/pyobjc_framework_SceneKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86d23456e4c7a7bb7bb49be2b98647678ac7a39955e6bb242e0ac125d8b770e8", size = 33108 }, - { url = "https://files.pythonhosted.org/packages/b8/7f/fef1cf3eaf1366a6f3f93c5a6b164acfdfdc2d15b3243b70763ac217ce03/pyobjc_framework_SceneKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d0a0d557167adddf27a42fb109a1dce29a22ff09aca34558fccd1c22f08ae2b4", size = 33130 }, + { url = "https://files.pythonhosted.org/packages/aa/4c/5ec624ae043fbbe15be2a989e3fc6cb08d992e0a5061450b84b33f96429c/pyobjc_framework_SceneKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86d23456e4c7a7bb7bb49be2b98647678ac7a39955e6bb242e0ac125d8b770e8", size = 33108, upload_time = "2025-01-14T18:58:43.577Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7f/fef1cf3eaf1366a6f3f93c5a6b164acfdfdc2d15b3243b70763ac217ce03/pyobjc_framework_SceneKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d0a0d557167adddf27a42fb109a1dce29a22ff09aca34558fccd1c22f08ae2b4", size = 33130, upload_time = "2025-01-14T18:58:44.549Z" }, ] [[package]] @@ -3804,10 +3811,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coremedia" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046 } +sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046, upload_time = "2025-01-14T19:05:16.834Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/aa/d6d0818564570065411874cbe3de86dee105dc9906161c0584009a1a63bc/pyobjc_framework_ScreenCaptureKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38468e833ec1498778bd33ce30578afed2e13ac14c73e8e6290ff06a2e0c50d8", size = 11110 }, - { url = "https://files.pythonhosted.org/packages/27/61/557e725aef9ad76a1a7c48b361f8c5636a606cbaf9ba520ff8f69d3cf791/pyobjc_framework_ScreenCaptureKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7d8a83dcc0950699242677cfefda545b9c0a0567111f8f3d3df1cf6ed75ea480", size = 11121 }, + { url = "https://files.pythonhosted.org/packages/af/aa/d6d0818564570065411874cbe3de86dee105dc9906161c0584009a1a63bc/pyobjc_framework_ScreenCaptureKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38468e833ec1498778bd33ce30578afed2e13ac14c73e8e6290ff06a2e0c50d8", size = 11110, upload_time = "2025-01-14T18:58:51.475Z" }, + { url = "https://files.pythonhosted.org/packages/27/61/557e725aef9ad76a1a7c48b361f8c5636a606cbaf9ba520ff8f69d3cf791/pyobjc_framework_ScreenCaptureKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7d8a83dcc0950699242677cfefda545b9c0a0567111f8f3d3df1cf6ed75ea480", size = 11121, upload_time = "2025-01-14T18:58:53.055Z" }, ] [[package]] @@ -3818,10 +3825,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774, upload_time = "2025-01-14T19:05:19.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ab/f17cd36458e6cf6d64c412128641edcfc220b8147283f6b34ef56c7db111/pyobjc_framework_ScreenSaver-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:436357c822d87220df64912da04b421e82a5e1e6464d48f2dbccc69529d19cd3", size = 8445 }, - { url = "https://files.pythonhosted.org/packages/52/57/300b641e929741a5d38cf80c74496918be1d2fe5e210d3fceb3e768747b2/pyobjc_framework_ScreenSaver-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:03b12e89bc164cb01527ca795f3f590f286d15de6ee0e4ff1d36705740d6d72f", size = 8372 }, + { url = "https://files.pythonhosted.org/packages/d7/ab/f17cd36458e6cf6d64c412128641edcfc220b8147283f6b34ef56c7db111/pyobjc_framework_ScreenSaver-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:436357c822d87220df64912da04b421e82a5e1e6464d48f2dbccc69529d19cd3", size = 8445, upload_time = "2025-01-14T18:58:59.299Z" }, + { url = "https://files.pythonhosted.org/packages/52/57/300b641e929741a5d38cf80c74496918be1d2fe5e210d3fceb3e768747b2/pyobjc_framework_ScreenSaver-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:03b12e89bc164cb01527ca795f3f590f286d15de6ee0e4ff1d36705740d6d72f", size = 8372, upload_time = "2025-01-14T18:59:00.358Z" }, ] [[package]] @@ -3832,10 +3839,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398 } +sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398, upload_time = "2025-01-14T19:05:21.547Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/7a/8df61f80725e993fd0dc1a111217de6a8efec35b02a4796749de0b7e8c34/pyobjc_framework_ScreenTime-11.0-py2.py3-none-any.whl", hash = "sha256:723938c7d47e3c5c1c0f79010a01139762384bd0c03c51ee7a4736fc3f128fed", size = 3721 }, - { url = "https://files.pythonhosted.org/packages/c4/62/2f86cedd4cc439625976848832c1d1571fcb69cc087dd71c9cf09e793db5/pyobjc_framework_ScreenTime-11.0-py3-none-any.whl", hash = "sha256:45db846ec9249cab90e86cbb31cf70e13800305b7c74819ab681a91854c91df2", size = 3790 }, + { url = "https://files.pythonhosted.org/packages/40/7a/8df61f80725e993fd0dc1a111217de6a8efec35b02a4796749de0b7e8c34/pyobjc_framework_ScreenTime-11.0-py2.py3-none-any.whl", hash = "sha256:723938c7d47e3c5c1c0f79010a01139762384bd0c03c51ee7a4736fc3f128fed", size = 3721, upload_time = "2025-01-14T18:59:04.027Z" }, + { url = "https://files.pythonhosted.org/packages/c4/62/2f86cedd4cc439625976848832c1d1571fcb69cc087dd71c9cf09e793db5/pyobjc_framework_ScreenTime-11.0-py3-none-any.whl", hash = "sha256:45db846ec9249cab90e86cbb31cf70e13800305b7c74819ab681a91854c91df2", size = 3790, upload_time = "2025-01-14T18:59:06.363Z" }, ] [[package]] @@ -3846,10 +3853,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626 } +sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626, upload_time = "2025-01-14T19:05:22.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/2c/2fd33c0318a8fe35f00f0089a44a2c27d4d0fd0b4b5e13628051a4d8c9d3/pyobjc_framework_ScriptingBridge-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c98d080446aa8ba4074e43eb0be1feed96781dbc0718496f172fcd20e84a9158", size = 8209 }, - { url = "https://files.pythonhosted.org/packages/93/3b/b2b721248e951eef6b7e6b25cb3a1d6683702235bc73683d0239f068d2df/pyobjc_framework_ScriptingBridge-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:23a4b2e2e57b7b4d992777ea9efb15273ccd8e8105385143dab9bd5a10962317", size = 8238 }, + { url = "https://files.pythonhosted.org/packages/7d/2c/2fd33c0318a8fe35f00f0089a44a2c27d4d0fd0b4b5e13628051a4d8c9d3/pyobjc_framework_ScriptingBridge-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c98d080446aa8ba4074e43eb0be1feed96781dbc0718496f172fcd20e84a9158", size = 8209, upload_time = "2025-01-14T18:59:11.107Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/b2b721248e951eef6b7e6b25cb3a1d6683702235bc73683d0239f068d2df/pyobjc_framework_ScriptingBridge-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:23a4b2e2e57b7b4d992777ea9efb15273ccd8e8105385143dab9bd5a10962317", size = 8238, upload_time = "2025-01-14T18:59:13.27Z" }, ] [[package]] @@ -3860,10 +3867,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-coreservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182 } +sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182, upload_time = "2025-01-14T19:05:24.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/d4/64fa608b5d91859b11c26ceca83a41d2bf1d0dcbf1d9df847bab5a52ccc8/pyobjc_framework_SearchKit-11.0-py2.py3-none-any.whl", hash = "sha256:332f9d30ec3b223efaac681fbdd923ba660575e241abb4ed5e03207c97799530", size = 3633 }, - { url = "https://files.pythonhosted.org/packages/93/e2/83e94c505c5436821982d724cc890f74d717f9473782f7278ce78634685d/pyobjc_framework_SearchKit-11.0-py3-none-any.whl", hash = "sha256:5f4304cb77c327b28ac0f7ec9b99313075afd742091d39368eb64f076bb7d141", size = 3699 }, + { url = "https://files.pythonhosted.org/packages/f2/d4/64fa608b5d91859b11c26ceca83a41d2bf1d0dcbf1d9df847bab5a52ccc8/pyobjc_framework_SearchKit-11.0-py2.py3-none-any.whl", hash = "sha256:332f9d30ec3b223efaac681fbdd923ba660575e241abb4ed5e03207c97799530", size = 3633, upload_time = "2025-01-14T18:59:18.343Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/83e94c505c5436821982d724cc890f74d717f9473782f7278ce78634685d/pyobjc_framework_SearchKit-11.0-py3-none-any.whl", hash = "sha256:5f4304cb77c327b28ac0f7ec9b99313075afd742091d39368eb64f076bb7d141", size = 3699, upload_time = "2025-01-14T18:59:20.754Z" }, ] [[package]] @@ -3874,10 +3881,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059 } +sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059, upload_time = "2025-01-14T19:05:26.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/d8/092940f8c46cf09000a9d026e9854772846d5335e3e8a44d0a81aa1f359e/pyobjc_framework_Security-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:93bc23630563de2551ac49048af010ac9cb40f927cc25c898b7cc48550ccd526", size = 41499 }, - { url = "https://files.pythonhosted.org/packages/0b/fc/8710bbe80b825c97ecc312aaead3b0f606a23b62b895f6e0a07df8bfeeae/pyobjc_framework_Security-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:421e03b8560ed296a7f5ee67f42f5f978f8c7959d65c8fec99cd77dc65786355", size = 41523 }, + { url = "https://files.pythonhosted.org/packages/fa/d8/092940f8c46cf09000a9d026e9854772846d5335e3e8a44d0a81aa1f359e/pyobjc_framework_Security-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:93bc23630563de2551ac49048af010ac9cb40f927cc25c898b7cc48550ccd526", size = 41499, upload_time = "2025-01-14T18:59:22.819Z" }, + { url = "https://files.pythonhosted.org/packages/0b/fc/8710bbe80b825c97ecc312aaead3b0f606a23b62b895f6e0a07df8bfeeae/pyobjc_framework_Security-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:421e03b8560ed296a7f5ee67f42f5f978f8c7959d65c8fec99cd77dc65786355", size = 41523, upload_time = "2025-01-14T18:59:25.368Z" }, ] [[package]] @@ -3889,10 +3896,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-security" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526 } +sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526, upload_time = "2025-01-14T19:05:27.275Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/41/50da30e87841c8b9ee1f17e9720dc9dbb2c2e59abac84fffe899ed5f9188/pyobjc_framework_SecurityFoundation-11.0-py2.py3-none-any.whl", hash = "sha256:8f8e43b91ae7cb45f3251c14c0c6caf5fdcdb93794176c4b118214a108ee2ef3", size = 3716 }, - { url = "https://files.pythonhosted.org/packages/cb/61/e73a61de62e31b33378ee635534228f4801b1554fbd89a47e0b36965908d/pyobjc_framework_SecurityFoundation-11.0-py3-none-any.whl", hash = "sha256:1fa89969fbf7a4fd57214388a43f7ed6b6b1fd0c0ec7aa77752444eb1604143c", size = 3787 }, + { url = "https://files.pythonhosted.org/packages/f8/41/50da30e87841c8b9ee1f17e9720dc9dbb2c2e59abac84fffe899ed5f9188/pyobjc_framework_SecurityFoundation-11.0-py2.py3-none-any.whl", hash = "sha256:8f8e43b91ae7cb45f3251c14c0c6caf5fdcdb93794176c4b118214a108ee2ef3", size = 3716, upload_time = "2025-01-14T18:59:29.79Z" }, + { url = "https://files.pythonhosted.org/packages/cb/61/e73a61de62e31b33378ee635534228f4801b1554fbd89a47e0b36965908d/pyobjc_framework_SecurityFoundation-11.0-py3-none-any.whl", hash = "sha256:1fa89969fbf7a4fd57214388a43f7ed6b6b1fd0c0ec7aa77752444eb1604143c", size = 3787, upload_time = "2025-01-14T18:59:30.764Z" }, ] [[package]] @@ -3904,10 +3911,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-security" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118, upload_time = "2025-01-14T19:05:28.569Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/a96da5f43da5a9d0e5d016bc672a4dca09f88d091c96d9ecff5f753ad1d5/pyobjc_framework_SecurityInterface-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2771dae043c8aa278887f96c7d206957164c7a81a562fa391bf0b9316d6755eb", size = 10706 }, - { url = "https://files.pythonhosted.org/packages/50/86/fc41dcf8f5300ad2c6508568535d9c0a83b412b0a4a961616441c8acf10f/pyobjc_framework_SecurityInterface-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6453732f7608d514e8f7005d80d238422cbebc4ab4d6d6fed1e51175f9f7244f", size = 10781 }, + { url = "https://files.pythonhosted.org/packages/88/5f/a96da5f43da5a9d0e5d016bc672a4dca09f88d091c96d9ecff5f753ad1d5/pyobjc_framework_SecurityInterface-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2771dae043c8aa278887f96c7d206957164c7a81a562fa391bf0b9316d6755eb", size = 10706, upload_time = "2025-01-14T18:59:32.632Z" }, + { url = "https://files.pythonhosted.org/packages/50/86/fc41dcf8f5300ad2c6508568535d9c0a83b412b0a4a961616441c8acf10f/pyobjc_framework_SecurityInterface-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6453732f7608d514e8f7005d80d238422cbebc4ab4d6d6fed1e51175f9f7244f", size = 10781, upload_time = "2025-01-14T18:59:33.832Z" }, ] [[package]] @@ -3919,10 +3926,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060 } +sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060, upload_time = "2025-01-14T19:05:29.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/eb/e0d60b3e233860a237fdddd44ab961c9115c33e947058d73c222dafc50af/pyobjc_framework_SensitiveContentAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:e19d2edc807f98aef31fa4db5472a509cf90523436c971d1095a000b0e357058", size = 3791 }, - { url = "https://files.pythonhosted.org/packages/c4/1c/fb2138cf08cd0215ea4f78032871a1d89e7e41d9fad18b55e937f0577c03/pyobjc_framework_SensitiveContentAnalysis-11.0-py3-none-any.whl", hash = "sha256:027bd0be0785f7aea3bfd56ff7c3496e5d383211122393c599c28ea392675589", size = 3863 }, + { url = "https://files.pythonhosted.org/packages/3d/eb/e0d60b3e233860a237fdddd44ab961c9115c33e947058d73c222dafc50af/pyobjc_framework_SensitiveContentAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:e19d2edc807f98aef31fa4db5472a509cf90523436c971d1095a000b0e357058", size = 3791, upload_time = "2025-01-14T18:59:39.563Z" }, + { url = "https://files.pythonhosted.org/packages/c4/1c/fb2138cf08cd0215ea4f78032871a1d89e7e41d9fad18b55e937f0577c03/pyobjc_framework_SensitiveContentAnalysis-11.0-py3-none-any.whl", hash = "sha256:027bd0be0785f7aea3bfd56ff7c3496e5d383211122393c599c28ea392675589", size = 3863, upload_time = "2025-01-14T18:59:40.548Z" }, ] [[package]] @@ -3933,10 +3940,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882 } +sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882, upload_time = "2025-01-14T19:05:30.537Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/35/cbac7db272d0e5e71b300be1517b0a1dc7cf035944675eaed7066d41e883/pyobjc_framework_ServiceManagement-11.0-py2.py3-none-any.whl", hash = "sha256:35cfd7a369a120fa55e64b719a2dda00295b2cc6ddab16ffa8939f4326d1b37d", size = 5254 }, - { url = "https://files.pythonhosted.org/packages/b3/40/26c5d63d131e3e415815bfbb4bd035ba10d45f0d87733646221966871b6b/pyobjc_framework_ServiceManagement-11.0-py3-none-any.whl", hash = "sha256:7ec19c9632f67d589ad37815d001e8e443d92e75001c370486a1070a4359e166", size = 5322 }, + { url = "https://files.pythonhosted.org/packages/5b/35/cbac7db272d0e5e71b300be1517b0a1dc7cf035944675eaed7066d41e883/pyobjc_framework_ServiceManagement-11.0-py2.py3-none-any.whl", hash = "sha256:35cfd7a369a120fa55e64b719a2dda00295b2cc6ddab16ffa8939f4326d1b37d", size = 5254, upload_time = "2025-01-14T18:59:41.438Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/26c5d63d131e3e415815bfbb4bd035ba10d45f0d87733646221966871b6b/pyobjc_framework_ServiceManagement-11.0-py3-none-any.whl", hash = "sha256:7ec19c9632f67d589ad37815d001e8e443d92e75001c370486a1070a4359e166", size = 5322, upload_time = "2025-01-14T18:59:42.585Z" }, ] [[package]] @@ -3947,10 +3954,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-sharedwithyoucore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696 } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696, upload_time = "2025-01-14T19:05:31.396Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/ab/391ef0de3021997ec9a12d8044c0b7e884780a9bead7f847254e06d0f075/pyobjc_framework_SharedWithYou-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dac74375d3dc18d67cae46f3f16a45cef699b1976a4012827c0f15256da55df", size = 8606 }, - { url = "https://files.pythonhosted.org/packages/cf/04/6a3eb12bf9c35f3063be678f36430beb92b7e2683f4b952596396473a74d/pyobjc_framework_SharedWithYou-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6076a0893a3597e054918c136f3391671a225a37fe1b1a070046817e3a232954", size = 8629 }, + { url = "https://files.pythonhosted.org/packages/3c/ab/391ef0de3021997ec9a12d8044c0b7e884780a9bead7f847254e06d0f075/pyobjc_framework_SharedWithYou-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dac74375d3dc18d67cae46f3f16a45cef699b1976a4012827c0f15256da55df", size = 8606, upload_time = "2025-01-14T18:59:44.537Z" }, + { url = "https://files.pythonhosted.org/packages/cf/04/6a3eb12bf9c35f3063be678f36430beb92b7e2683f4b952596396473a74d/pyobjc_framework_SharedWithYou-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6076a0893a3597e054918c136f3391671a225a37fe1b1a070046817e3a232954", size = 8629, upload_time = "2025-01-14T18:59:45.579Z" }, ] [[package]] @@ -3961,10 +3968,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877 } +sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877, upload_time = "2025-01-14T19:05:32.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/40/69ae712e223991cd975c1f8ba2b00a5aa4c129ac0e76838b4d936740e4c7/pyobjc_framework_SharedWithYouCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:46cd00a97c5fec747ef057000daa88495699ea5d5d6fe1f302bfb89b2d431645", size = 8366 }, - { url = "https://files.pythonhosted.org/packages/c2/ce/500ad643f2d07e8ef065e8ddc5a08954f5d59cc199c89b700581eaf821ee/pyobjc_framework_SharedWithYouCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8b5f180371a63da718fe6c3b58e7613c6b2adf9b483cefbf6d9467eb8ac2f0ca", size = 8380 }, + { url = "https://files.pythonhosted.org/packages/21/40/69ae712e223991cd975c1f8ba2b00a5aa4c129ac0e76838b4d936740e4c7/pyobjc_framework_SharedWithYouCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:46cd00a97c5fec747ef057000daa88495699ea5d5d6fe1f302bfb89b2d431645", size = 8366, upload_time = "2025-01-14T18:59:55.641Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ce/500ad643f2d07e8ef065e8ddc5a08954f5d59cc199c89b700581eaf821ee/pyobjc_framework_SharedWithYouCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8b5f180371a63da718fe6c3b58e7613c6b2adf9b483cefbf6d9467eb8ac2f0ca", size = 8380, upload_time = "2025-01-14T18:59:56.546Z" }, ] [[package]] @@ -3975,10 +3982,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172 } +sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172, upload_time = "2025-01-14T19:05:34.497Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/81/edfcd4be626aae356dd1b991f521eaeffa1798e91ddae9e7d9ae8ed371d1/pyobjc_framework_ShazamKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ecdc2392d7e8d6e2540c7ad3073a229d08b0818c5dd044a26c93b765ce9868aa", size = 8411 }, - { url = "https://files.pythonhosted.org/packages/e1/f7/f3d2ae7a604e3e3c0de93ed229895be6757edfa0cc76f2a44670f28a81c8/pyobjc_framework_ShazamKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ef79d863cc7d4023aa552f55d4120653eceed862baf1edba8e08b1af10fab036", size = 8419 }, + { url = "https://files.pythonhosted.org/packages/05/81/edfcd4be626aae356dd1b991f521eaeffa1798e91ddae9e7d9ae8ed371d1/pyobjc_framework_ShazamKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ecdc2392d7e8d6e2540c7ad3073a229d08b0818c5dd044a26c93b765ce9868aa", size = 8411, upload_time = "2025-01-14T19:00:02.908Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/f3d2ae7a604e3e3c0de93ed229895be6757edfa0cc76f2a44670f28a81c8/pyobjc_framework_ShazamKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ef79d863cc7d4023aa552f55d4120653eceed862baf1edba8e08b1af10fab036", size = 8419, upload_time = "2025-01-14T19:00:05.081Z" }, ] [[package]] @@ -3989,10 +3996,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806, upload_time = "2025-01-14T19:05:35.394Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/1d/2cc0f753ac8b1f5c15cfa9201d8584ff4de6dc940fc954cd9c52d1a615f9/pyobjc_framework_Social-11.0-py2.py3-none-any.whl", hash = "sha256:aa379009738afb0d6abc0347e8189f7f316109e9dfcb904f7f14e6b7c3d5bad8", size = 4362 }, - { url = "https://files.pythonhosted.org/packages/a8/25/b762b1f9429f8ea0df754e7d58bafd48d73e5527b0423e67570661a7907e/pyobjc_framework_Social-11.0-py3-none-any.whl", hash = "sha256:94db183e8b3ad21272a1ba24e9cda763d603c6021fd80a96d00ce78b6b94e1c2", size = 4428 }, + { url = "https://files.pythonhosted.org/packages/46/1d/2cc0f753ac8b1f5c15cfa9201d8584ff4de6dc940fc954cd9c52d1a615f9/pyobjc_framework_Social-11.0-py2.py3-none-any.whl", hash = "sha256:aa379009738afb0d6abc0347e8189f7f316109e9dfcb904f7f14e6b7c3d5bad8", size = 4362, upload_time = "2025-01-14T19:00:10.058Z" }, + { url = "https://files.pythonhosted.org/packages/a8/25/b762b1f9429f8ea0df754e7d58bafd48d73e5527b0423e67570661a7907e/pyobjc_framework_Social-11.0-py3-none-any.whl", hash = "sha256:94db183e8b3ad21272a1ba24e9cda763d603c6021fd80a96d00ce78b6b94e1c2", size = 4428, upload_time = "2025-01-14T19:00:11.242Z" }, ] [[package]] @@ -4003,10 +4010,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789 } +sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789, upload_time = "2025-01-14T19:05:36.576Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/d4/91afb41c514d1e236567b971a981f96c1d20f16eb0658256369c53a4bf45/pyobjc_framework_SoundAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:5969096cadb07f9ba9855cedf6f53674ddb030a324b4981091834d1b31c8c27e", size = 4111 }, - { url = "https://files.pythonhosted.org/packages/af/7a/f960ad1e727f6d917e6c84b7383f3eacbb2948bc60396be3bce40cbd8128/pyobjc_framework_SoundAnalysis-11.0-py3-none-any.whl", hash = "sha256:70f70923756e118203cde4ac25083a34ead69a6034baed9c694a36f5fe2325f3", size = 4182 }, + { url = "https://files.pythonhosted.org/packages/ab/d4/91afb41c514d1e236567b971a981f96c1d20f16eb0658256369c53a4bf45/pyobjc_framework_SoundAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:5969096cadb07f9ba9855cedf6f53674ddb030a324b4981091834d1b31c8c27e", size = 4111, upload_time = "2025-01-14T19:00:13.327Z" }, + { url = "https://files.pythonhosted.org/packages/af/7a/f960ad1e727f6d917e6c84b7383f3eacbb2948bc60396be3bce40cbd8128/pyobjc_framework_SoundAnalysis-11.0-py3-none-any.whl", hash = "sha256:70f70923756e118203cde4ac25083a34ead69a6034baed9c694a36f5fe2325f3", size = 4182, upload_time = "2025-01-14T19:00:15.68Z" }, ] [[package]] @@ -4017,10 +4024,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620 } +sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620, upload_time = "2025-01-14T19:05:38.391Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/85/e989076ff0cd40c7cfb3ed7d621703de11bfd8286f1729aca759db1f42a3/pyobjc_framework_Speech-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:353179210683e38bfbd675df6a35eec46b30ce30b7291bcb07a5cadaf11a3bd7", size = 9016 }, - { url = "https://files.pythonhosted.org/packages/00/03/827acde068787c2318981e2bfef2c3cadbe8552434ccc0634b30084ef914/pyobjc_framework_Speech-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:134e08025f4638e428602f7e16bbec94b00477eec090316138d758a86e10fd5f", size = 9037 }, + { url = "https://files.pythonhosted.org/packages/b0/85/e989076ff0cd40c7cfb3ed7d621703de11bfd8286f1729aca759db1f42a3/pyobjc_framework_Speech-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:353179210683e38bfbd675df6a35eec46b30ce30b7291bcb07a5cadaf11a3bd7", size = 9016, upload_time = "2025-01-14T19:00:17.661Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/827acde068787c2318981e2bfef2c3cadbe8552434ccc0634b30084ef914/pyobjc_framework_Speech-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:134e08025f4638e428602f7e16bbec94b00477eec090316138d758a86e10fd5f", size = 9037, upload_time = "2025-01-14T19:00:21.186Z" }, ] [[package]] @@ -4032,10 +4039,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851, upload_time = "2025-01-14T19:05:39.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/80/319f156ac6f6cab0dbc85881d81a74d4a7f17913256338683ae8d9ed56c4/pyobjc_framework_SpriteKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d0971a7a85786edc521ab897bdb0c78696278e6417bf389abdfe2151358e854", size = 18077 }, - { url = "https://files.pythonhosted.org/packages/bb/09/303d76844a10745cdbac1ff76c2c8630c1ef46455014562dc79aaa72a6e3/pyobjc_framework_SpriteKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0da5f2b52636a2f04fc38a123fed9d7f8d6fd353df027c51c0bfc91e244a9d2b", size = 18145 }, + { url = "https://files.pythonhosted.org/packages/e1/80/319f156ac6f6cab0dbc85881d81a74d4a7f17913256338683ae8d9ed56c4/pyobjc_framework_SpriteKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d0971a7a85786edc521ab897bdb0c78696278e6417bf389abdfe2151358e854", size = 18077, upload_time = "2025-01-14T19:00:26.815Z" }, + { url = "https://files.pythonhosted.org/packages/bb/09/303d76844a10745cdbac1ff76c2c8630c1ef46455014562dc79aaa72a6e3/pyobjc_framework_SpriteKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0da5f2b52636a2f04fc38a123fed9d7f8d6fd353df027c51c0bfc91e244a9d2b", size = 18145, upload_time = "2025-01-14T19:00:27.956Z" }, ] [[package]] @@ -4046,10 +4053,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855 } +sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855, upload_time = "2025-01-14T19:05:41.605Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/40/af53ad7781515866003c2c71056a053d2f033cf2aa31920a8a1fdb829d7a/pyobjc_framework_StoreKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1d51a05a5e0277c542978b1f5a6aa33331359de7c0a2cf0ad922760b36e5066a", size = 11655 }, - { url = "https://files.pythonhosted.org/packages/f3/11/ba3259d3b22980e08c5e8255a48cc97180bec47d72ffbbd41ab699df39b1/pyobjc_framework_StoreKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:29269183e91043bbfee79851ae712073feba1e10845b8deeb7e6aaa20cfb3cf4", size = 11680 }, + { url = "https://files.pythonhosted.org/packages/ab/40/af53ad7781515866003c2c71056a053d2f033cf2aa31920a8a1fdb829d7a/pyobjc_framework_StoreKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1d51a05a5e0277c542978b1f5a6aa33331359de7c0a2cf0ad922760b36e5066a", size = 11655, upload_time = "2025-01-14T19:00:32.894Z" }, + { url = "https://files.pythonhosted.org/packages/f3/11/ba3259d3b22980e08c5e8255a48cc97180bec47d72ffbbd41ab699df39b1/pyobjc_framework_StoreKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:29269183e91043bbfee79851ae712073feba1e10845b8deeb7e6aaa20cfb3cf4", size = 11680, upload_time = "2025-01-14T19:00:35.36Z" }, ] [[package]] @@ -4060,10 +4067,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682 } +sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682, upload_time = "2025-01-14T19:05:45.727Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/ff/341d44f5347d48491682bece366444f3e230e33109266dcc6a17e6a7fc3d/pyobjc_framework_Symbols-11.0-py2.py3-none-any.whl", hash = "sha256:f1490823f40a8a540ac10628190695f27a717343914fe5db5fafa500f7c7bf44", size = 3263 }, - { url = "https://files.pythonhosted.org/packages/94/a4/c21353872a2fc643206a44ac55b92b5b7533cdb2cb26c44a9048debc295a/pyobjc_framework_Symbols-11.0-py3-none-any.whl", hash = "sha256:0919e85fcf6f420f61d8d9a67cafa2ab4678666441ef4f001b31f5457900b314", size = 3335 }, + { url = "https://files.pythonhosted.org/packages/66/ff/341d44f5347d48491682bece366444f3e230e33109266dcc6a17e6a7fc3d/pyobjc_framework_Symbols-11.0-py2.py3-none-any.whl", hash = "sha256:f1490823f40a8a540ac10628190695f27a717343914fe5db5fafa500f7c7bf44", size = 3263, upload_time = "2025-01-14T19:00:41.055Z" }, + { url = "https://files.pythonhosted.org/packages/94/a4/c21353872a2fc643206a44ac55b92b5b7533cdb2cb26c44a9048debc295a/pyobjc_framework_Symbols-11.0-py3-none-any.whl", hash = "sha256:0919e85fcf6f420f61d8d9a67cafa2ab4678666441ef4f001b31f5457900b314", size = 3335, upload_time = "2025-01-14T19:00:43.294Z" }, ] [[package]] @@ -4075,10 +4082,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coredata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453 } +sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453, upload_time = "2025-01-14T19:05:46.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/9b/484db4eed6b1e29e0d69275bd459ab21a6b3f98e8b2ce61beeb9971303ca/pyobjc_framework_SyncServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89a398df6518cff1c63b7cccf3025e388f3ef299645734112c5aa1ac5f7ca30a", size = 13989 }, - { url = "https://files.pythonhosted.org/packages/8d/d8/dc86d708434b7cb59825c56549e64b118ba4b8584d2eb5a1514d1cd5d1bd/pyobjc_framework_SyncServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e870e82ed34c43607cc50dbae57a81dd419b75abc06670630cbbf41ae6e1402c", size = 14008 }, + { url = "https://files.pythonhosted.org/packages/15/9b/484db4eed6b1e29e0d69275bd459ab21a6b3f98e8b2ce61beeb9971303ca/pyobjc_framework_SyncServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89a398df6518cff1c63b7cccf3025e388f3ef299645734112c5aa1ac5f7ca30a", size = 13989, upload_time = "2025-01-14T19:00:45.216Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d8/dc86d708434b7cb59825c56549e64b118ba4b8584d2eb5a1514d1cd5d1bd/pyobjc_framework_SyncServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e870e82ed34c43607cc50dbae57a81dd419b75abc06670630cbbf41ae6e1402c", size = 14008, upload_time = "2025-01-14T19:00:46.188Z" }, ] [[package]] @@ -4089,10 +4096,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949 } +sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949, upload_time = "2025-01-14T19:05:47.466Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/8f/1b5f7e8e848d2c84204da08d5c63e42feff86b26cd508da7a4f95960b842/pyobjc_framework_SystemConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89d3c54abedcedbc2ce52c31ff4878251ca54a8535407ed6bd6584ce099c148b", size = 21836 }, - { url = "https://files.pythonhosted.org/packages/6d/49/8660b3d0a46ac2f88e73cec3d10e21885b107f54635680ef0c677ac5cf3e/pyobjc_framework_SystemConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8cbcb9662dbb5a034cfc5a44adaf2a0226a2985ae299a4ef4fd75bb49f30f5a0", size = 21727 }, + { url = "https://files.pythonhosted.org/packages/28/8f/1b5f7e8e848d2c84204da08d5c63e42feff86b26cd508da7a4f95960b842/pyobjc_framework_SystemConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89d3c54abedcedbc2ce52c31ff4878251ca54a8535407ed6bd6584ce099c148b", size = 21836, upload_time = "2025-01-14T19:00:51.698Z" }, + { url = "https://files.pythonhosted.org/packages/6d/49/8660b3d0a46ac2f88e73cec3d10e21885b107f54635680ef0c677ac5cf3e/pyobjc_framework_SystemConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8cbcb9662dbb5a034cfc5a44adaf2a0226a2985ae299a4ef4fd75bb49f30f5a0", size = 21727, upload_time = "2025-01-14T19:00:52.685Z" }, ] [[package]] @@ -4103,10 +4110,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531 } +sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531, upload_time = "2025-01-14T19:05:48.463Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/3c/8f91b89554ef3127e037d90b3ef83c77a994bb889b7884a995756cd06b63/pyobjc_framework_SystemExtensions-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7a2ec417fa0d383cc066bc292541aa78fd2aec9cca83a98d41b7982f185d1f7", size = 8975 }, - { url = "https://files.pythonhosted.org/packages/21/8c/cf2a018b5f1ecd216f8cb26a3b6fbe590d08de81a6c6b4658e001a203886/pyobjc_framework_SystemExtensions-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:62b99c6bd88bce642960fc2b9d5903fbfca680d16be9a4565a883eb4ba17ca5e", size = 8999 }, + { url = "https://files.pythonhosted.org/packages/15/3c/8f91b89554ef3127e037d90b3ef83c77a994bb889b7884a995756cd06b63/pyobjc_framework_SystemExtensions-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7a2ec417fa0d383cc066bc292541aa78fd2aec9cca83a98d41b7982f185d1f7", size = 8975, upload_time = "2025-01-14T19:00:57.822Z" }, + { url = "https://files.pythonhosted.org/packages/21/8c/cf2a018b5f1ecd216f8cb26a3b6fbe590d08de81a6c6b4658e001a203886/pyobjc_framework_SystemExtensions-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:62b99c6bd88bce642960fc2b9d5903fbfca680d16be9a4565a883eb4ba17ca5e", size = 8999, upload_time = "2025-01-14T19:00:58.865Z" }, ] [[package]] @@ -4117,10 +4124,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820, upload_time = "2025-01-14T19:05:49.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/a9/908184da457e33a110de7d2d262efa69beaba6db243342df5654da03566b/pyobjc_framework_ThreadNetwork-11.0-py2.py3-none-any.whl", hash = "sha256:950d46a009cb992b12dbd8169a0450d8cc101fc982e03e6543078c6d7790e353", size = 3700 }, - { url = "https://files.pythonhosted.org/packages/59/d4/4694fc7a627d2b6b37c51433ba7f02a39a283a445dc77349b82fe24534f1/pyobjc_framework_ThreadNetwork-11.0-py3-none-any.whl", hash = "sha256:1218649e4f488ca411af13b74f1dee1e7a178169e0f5963342ba8a7c46037ea7", size = 3770 }, + { url = "https://files.pythonhosted.org/packages/55/a9/908184da457e33a110de7d2d262efa69beaba6db243342df5654da03566b/pyobjc_framework_ThreadNetwork-11.0-py2.py3-none-any.whl", hash = "sha256:950d46a009cb992b12dbd8169a0450d8cc101fc982e03e6543078c6d7790e353", size = 3700, upload_time = "2025-01-14T19:01:03.254Z" }, + { url = "https://files.pythonhosted.org/packages/59/d4/4694fc7a627d2b6b37c51433ba7f02a39a283a445dc77349b82fe24534f1/pyobjc_framework_ThreadNetwork-11.0-py3-none-any.whl", hash = "sha256:1218649e4f488ca411af13b74f1dee1e7a178169e0f5963342ba8a7c46037ea7", size = 3770, upload_time = "2025-01-14T19:01:05.456Z" }, ] [[package]] @@ -4131,10 +4138,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860 } +sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860, upload_time = "2025-01-14T19:05:50.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/f2/094888af07fb7f0443996e5d91915e74b87e8705b599b68b516a0e94a63d/pyobjc_framework_UniformTypeIdentifiers-11.0-py2.py3-none-any.whl", hash = "sha256:acffb86e8b03b66c49274236b3df3a254cacd32b9f25bd7a5bd59baaaf738624", size = 4841 }, - { url = "https://files.pythonhosted.org/packages/88/9c/4cc0522cc546e6a3bf8a921e3a9f0ed078e3cf907d616760d9f3d7754919/pyobjc_framework_UniformTypeIdentifiers-11.0-py3-none-any.whl", hash = "sha256:a3097f186c7e231b19218a3ceecb3b70a8f2b2e9e642ef409dc7a195a30c869e", size = 4910 }, + { url = "https://files.pythonhosted.org/packages/82/f2/094888af07fb7f0443996e5d91915e74b87e8705b599b68b516a0e94a63d/pyobjc_framework_UniformTypeIdentifiers-11.0-py2.py3-none-any.whl", hash = "sha256:acffb86e8b03b66c49274236b3df3a254cacd32b9f25bd7a5bd59baaaf738624", size = 4841, upload_time = "2025-01-14T19:01:06.404Z" }, + { url = "https://files.pythonhosted.org/packages/88/9c/4cc0522cc546e6a3bf8a921e3a9f0ed078e3cf907d616760d9f3d7754919/pyobjc_framework_UniformTypeIdentifiers-11.0-py3-none-any.whl", hash = "sha256:a3097f186c7e231b19218a3ceecb3b70a8f2b2e9e642ef409dc7a195a30c869e", size = 4910, upload_time = "2025-01-14T19:01:07.393Z" }, ] [[package]] @@ -4145,10 +4152,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857 } +sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857, upload_time = "2025-01-14T19:05:52.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/bf/5545d5c9d0d10a603ad406a5ce727de6a47daace9c38d4484818611599f3/pyobjc_framework_UserNotifications-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bf78fa37f574f5b43db9b83ca02e82ab45803589f970042afdcd1cb8c01396d", size = 9483 }, - { url = "https://files.pythonhosted.org/packages/7a/1e/41f4d18120b2c006f756edde1845a2df45fdbd6957e540f8ebcfae25747f/pyobjc_framework_UserNotifications-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b4c06c3862405e103e964327581c28e5390a2d4cd0cef3d8e64afda03c9f431", size = 9506 }, + { url = "https://files.pythonhosted.org/packages/1f/bf/5545d5c9d0d10a603ad406a5ce727de6a47daace9c38d4484818611599f3/pyobjc_framework_UserNotifications-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bf78fa37f574f5b43db9b83ca02e82ab45803589f970042afdcd1cb8c01396d", size = 9483, upload_time = "2025-01-14T19:01:09.256Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/41f4d18120b2c006f756edde1845a2df45fdbd6957e540f8ebcfae25747f/pyobjc_framework_UserNotifications-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b4c06c3862405e103e964327581c28e5390a2d4cd0cef3d8e64afda03c9f431", size = 9506, upload_time = "2025-01-14T19:01:10.218Z" }, ] [[package]] @@ -4160,10 +4167,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-usernotifications" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369 } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369, upload_time = "2025-01-14T19:05:54.498Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f7/64c95c6f82e92bb1cbcb8d5c3658c79c954668627eef28f11e76025a3ed1/pyobjc_framework_UserNotificationsUI-11.0-py2.py3-none-any.whl", hash = "sha256:6185d9c9513b6a823cd72dcf40d2fb33bbf0f2c9a98528e0e112580b47ac3632", size = 3856 }, - { url = "https://files.pythonhosted.org/packages/eb/c3/e1d64c9e523b5192e0179b6723ee465e74d6c282104a49a67347d527a65d/pyobjc_framework_UserNotificationsUI-11.0-py3-none-any.whl", hash = "sha256:e4439e549265929ddad1feca7b062d00c2d3732470f349cb0d594705e0257919", size = 3932 }, + { url = "https://files.pythonhosted.org/packages/bb/f7/64c95c6f82e92bb1cbcb8d5c3658c79c954668627eef28f11e76025a3ed1/pyobjc_framework_UserNotificationsUI-11.0-py2.py3-none-any.whl", hash = "sha256:6185d9c9513b6a823cd72dcf40d2fb33bbf0f2c9a98528e0e112580b47ac3632", size = 3856, upload_time = "2025-01-14T19:01:15.43Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c3/e1d64c9e523b5192e0179b6723ee465e74d6c282104a49a67347d527a65d/pyobjc_framework_UserNotificationsUI-11.0-py3-none-any.whl", hash = "sha256:e4439e549265929ddad1feca7b062d00c2d3732470f349cb0d594705e0257919", size = 3932, upload_time = "2025-01-14T19:01:16.486Z" }, ] [[package]] @@ -4174,10 +4181,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100 } +sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100, upload_time = "2025-01-14T19:05:55.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/82/94650fe5cc68c0c32fe56fe22cd7eb2874b28f987a9e259fac12cbea7705/pyobjc_framework_VideoSubscriberAccount-11.0-py2.py3-none-any.whl", hash = "sha256:1deec8d5a0138ae51b5ca7bfb7f6fe1b0dc3cbb52db3111059708efa5f8a8d04", size = 4637 }, - { url = "https://files.pythonhosted.org/packages/61/54/1765507adad1b0c9bc6be10f09b249d425212bc0d9fef1efdfd872ee9807/pyobjc_framework_VideoSubscriberAccount-11.0-py3-none-any.whl", hash = "sha256:0095eddb5fc942f9e049bc4c683cf28c77ea60c60942552c3c48bf74c8fdca9b", size = 4709 }, + { url = "https://files.pythonhosted.org/packages/51/82/94650fe5cc68c0c32fe56fe22cd7eb2874b28f987a9e259fac12cbea7705/pyobjc_framework_VideoSubscriberAccount-11.0-py2.py3-none-any.whl", hash = "sha256:1deec8d5a0138ae51b5ca7bfb7f6fe1b0dc3cbb52db3111059708efa5f8a8d04", size = 4637, upload_time = "2025-01-14T19:01:17.365Z" }, + { url = "https://files.pythonhosted.org/packages/61/54/1765507adad1b0c9bc6be10f09b249d425212bc0d9fef1efdfd872ee9807/pyobjc_framework_VideoSubscriberAccount-11.0-py3-none-any.whl", hash = "sha256:0095eddb5fc942f9e049bc4c683cf28c77ea60c60942552c3c48bf74c8fdca9b", size = 4709, upload_time = "2025-01-14T19:01:18.349Z" }, ] [[package]] @@ -4190,10 +4197,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157, upload_time = "2025-01-14T19:05:56.135Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/ae/ff697840bdcf3530e8fba84e2a606813eda1ee90be074f12e2857460cebf/pyobjc_framework_VideoToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:12af56190e65c3b60c6ca14fe69045e5ffb5908ea1363580506eb32603b80855", size = 13446 }, - { url = "https://files.pythonhosted.org/packages/1e/ef/9e7230435da47016983a3c9ea7b1d5237b43fce2d8b2b923eb638b7694f5/pyobjc_framework_VideoToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ed7f073bd8dfecca0da6359d5cd871b2f39144883930bddd41ca818447de608", size = 13451 }, + { url = "https://files.pythonhosted.org/packages/44/ae/ff697840bdcf3530e8fba84e2a606813eda1ee90be074f12e2857460cebf/pyobjc_framework_VideoToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:12af56190e65c3b60c6ca14fe69045e5ffb5908ea1363580506eb32603b80855", size = 13446, upload_time = "2025-01-14T19:01:21.066Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ef/9e7230435da47016983a3c9ea7b1d5237b43fce2d8b2b923eb638b7694f5/pyobjc_framework_VideoToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ed7f073bd8dfecca0da6359d5cd871b2f39144883930bddd41ca818447de608", size = 13451, upload_time = "2025-01-14T19:01:22.009Z" }, ] [[package]] @@ -4204,10 +4211,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144 } +sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144, upload_time = "2025-01-14T19:05:57.086Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/c9/b2f8322d7ced14822270481be5b44f1846aa7c09b4b3cb52517dc1054f4b/pyobjc_framework_Virtualization-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334712792136ffcf3c63a63cea01ce33d60309a82721c95e25f0cc26b95f72cc", size = 13417 }, - { url = "https://files.pythonhosted.org/packages/1e/96/d64425811a4ef2c8b38914ea1a91bbd2aa6136bb79989e4821acd6d28e67/pyobjc_framework_Virtualization-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b848b1ab365906b11a507c8146e477c27d2bf56159d49d21fda15b93c2811ec", size = 13430 }, + { url = "https://files.pythonhosted.org/packages/6b/c9/b2f8322d7ced14822270481be5b44f1846aa7c09b4b3cb52517dc1054f4b/pyobjc_framework_Virtualization-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334712792136ffcf3c63a63cea01ce33d60309a82721c95e25f0cc26b95f72cc", size = 13417, upload_time = "2025-01-14T19:01:28.821Z" }, + { url = "https://files.pythonhosted.org/packages/1e/96/d64425811a4ef2c8b38914ea1a91bbd2aa6136bb79989e4821acd6d28e67/pyobjc_framework_Virtualization-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b848b1ab365906b11a507c8146e477c27d2bf56159d49d21fda15b93c2811ec", size = 13430, upload_time = "2025-01-14T19:01:29.733Z" }, ] [[package]] @@ -4220,10 +4227,10 @@ dependencies = [ { name = "pyobjc-framework-coreml" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175 } +sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175, upload_time = "2025-01-14T19:05:58.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/84/d23a745d46858409a1dca3e7f5cb3089c148ebb8d42e7a6289e1972ad650/pyobjc_framework_Vision-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ca7cc48332d804a02b5b17f31bed52dd4b7c323f9e4ff4b4e7ecd35d39cc0759", size = 21754 }, - { url = "https://files.pythonhosted.org/packages/3a/80/6db9fc2a3f8b991860156f4700f979ad8aa1e9617b0efa720ee3b52e3602/pyobjc_framework_Vision-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1b07aa867dda47d2a4883cd969e248039988b49190ba097cbe9747156b5d1f30", size = 17099 }, + { url = "https://files.pythonhosted.org/packages/7f/84/d23a745d46858409a1dca3e7f5cb3089c148ebb8d42e7a6289e1972ad650/pyobjc_framework_Vision-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ca7cc48332d804a02b5b17f31bed52dd4b7c323f9e4ff4b4e7ecd35d39cc0759", size = 21754, upload_time = "2025-01-14T19:01:35.504Z" }, + { url = "https://files.pythonhosted.org/packages/3a/80/6db9fc2a3f8b991860156f4700f979ad8aa1e9617b0efa720ee3b52e3602/pyobjc_framework_Vision-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1b07aa867dda47d2a4883cd969e248039988b49190ba097cbe9747156b5d1f30", size = 17099, upload_time = "2025-01-14T19:01:37.457Z" }, ] [[package]] @@ -4234,10 +4241,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210 } +sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210, upload_time = "2025-01-14T19:05:59.3Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/63/6f04faa75c4c39c54007b256a8e13838c1de213d487f561937d342ec2eac/pyobjc_framework_WebKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:163abaa5a665b59626ef20cdc3dcc5e2e3fcd9830d5fc328507e13f663acd0ed", size = 44940 }, - { url = "https://files.pythonhosted.org/packages/3e/61/934f03510e7f49454fbf6eeff8ad2eca5d8bfbe71aa4b8a034f8132af2fa/pyobjc_framework_WebKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2e4911519e94822011d99fdb9addf4a176f45a79808dab18dc303293f4590f7c", size = 44901 }, + { url = "https://files.pythonhosted.org/packages/47/63/6f04faa75c4c39c54007b256a8e13838c1de213d487f561937d342ec2eac/pyobjc_framework_WebKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:163abaa5a665b59626ef20cdc3dcc5e2e3fcd9830d5fc328507e13f663acd0ed", size = 44940, upload_time = "2025-01-14T19:01:44.396Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/934f03510e7f49454fbf6eeff8ad2eca5d8bfbe71aa4b8a034f8132af2fa/pyobjc_framework_WebKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2e4911519e94822011d99fdb9addf4a176f45a79808dab18dc303293f4590f7c", size = 44901, upload_time = "2025-01-14T19:01:45.476Z" }, ] [[package]] @@ -4249,18 +4256,18 @@ dependencies = [ { name = "platformdirs" }, { name = "pytools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510 } +sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload_time = "2025-01-22T00:16:58.421Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/ce/c40c6248b29195397a6e176615c24a8047cdd3afe847932a1f27603c1b14/pyopencl-2025.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:a302e4ee1bb19ff244f5ae2b5a83a98977daa13f31929a85f23723020a4bec01", size = 424117 }, - { url = "https://files.pythonhosted.org/packages/71/dd/8dd4e18396c705567be7eda65234932f8eb7e975cc15ae167265ed9c7d20/pyopencl-2025.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:48527fc5a250b9e89f2eaaa1c9423e1bc15ff181bd41ffa1e29e31890dc1d3b7", size = 408327 }, - { url = "https://files.pythonhosted.org/packages/47/7b/270c4e6765b675eaa97af8ff0c964e21dc74ac2a2f04e2c24431c91ce382/pyopencl-2025.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95490199c490e47b245b88e64e06f95272502d13810da172ac3b0f0340cf8715", size = 724636 }, - { url = "https://files.pythonhosted.org/packages/0d/55/996d7877793acfc340678a71dc98151a8c39dbb289cf24ecae08c0af68eb/pyopencl-2025.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd2cb3c031beeec93cf660c8809d6ad58a2cde7dcd95ae12b4b2da6ac8e2da41", size = 1173429 }, - { url = "https://files.pythonhosted.org/packages/3d/7c/d2a89b1c24c318375856e8b7611bc03ddf687134f68ddbb387496453eda8/pyopencl-2025.1-cp311-cp311-win_amd64.whl", hash = "sha256:d8d3cdb02dc8750a9cc11c5b37f219da1f61b4216af4a830eb52b451a0e9f9a7", size = 457877 }, - { url = "https://files.pythonhosted.org/packages/02/c0/d9536211ecfddd3bdf7eaa1658d085fcd92120061ac6f4e662a5062660ff/pyopencl-2025.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:88c564d94a5067ab6b9429a7d92b655254da8b2b5a33c7e30e10c5a0cf3154e1", size = 425706 }, - { url = "https://files.pythonhosted.org/packages/63/b9/3e6dd574cc9ffb2271be135ecdb36cd6aea70a1f74e80539b048072a256a/pyopencl-2025.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f204cd6974ca19e7ffe14f21afac9967b06a6718f3aecbbc4a4df313e8e7ebc2", size = 408163 }, - { url = "https://files.pythonhosted.org/packages/a4/4d/7f6f2e24b12585b81fd49f689d21ba62187656d061e3cb43840f12097dad/pyopencl-2025.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce8d1b3fd2046e89377b754117fdb3505f45edacfda6ad2b3a29670c0526ad1", size = 719348 }, - { url = "https://files.pythonhosted.org/packages/f0/45/3c93510819859e047d494dd8f4ed80c26378bb964a8e5e850cc079cc1f6e/pyopencl-2025.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb0b7b775424f0c4c929a00f09eb351075ea9e4d2b1c80afe37d09bec218ee9", size = 1170733 }, - { url = "https://files.pythonhosted.org/packages/91/ba/b745715085ef893fa7d1c7e04c95e3e554b6b27b2fb0800d6bbca563b43c/pyopencl-2025.1-cp312-cp312-win_amd64.whl", hash = "sha256:78f2a58d2e177793fb5a7b9c8a574e3a5f1d178c4df713969d1b08341c817d60", size = 457762 }, + { url = "https://files.pythonhosted.org/packages/99/ce/c40c6248b29195397a6e176615c24a8047cdd3afe847932a1f27603c1b14/pyopencl-2025.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:a302e4ee1bb19ff244f5ae2b5a83a98977daa13f31929a85f23723020a4bec01", size = 424117, upload_time = "2025-01-22T00:16:08.957Z" }, + { url = "https://files.pythonhosted.org/packages/71/dd/8dd4e18396c705567be7eda65234932f8eb7e975cc15ae167265ed9c7d20/pyopencl-2025.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:48527fc5a250b9e89f2eaaa1c9423e1bc15ff181bd41ffa1e29e31890dc1d3b7", size = 408327, upload_time = "2025-01-22T00:16:10.42Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/270c4e6765b675eaa97af8ff0c964e21dc74ac2a2f04e2c24431c91ce382/pyopencl-2025.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95490199c490e47b245b88e64e06f95272502d13810da172ac3b0f0340cf8715", size = 724636, upload_time = "2025-01-22T00:16:12.881Z" }, + { url = "https://files.pythonhosted.org/packages/0d/55/996d7877793acfc340678a71dc98151a8c39dbb289cf24ecae08c0af68eb/pyopencl-2025.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd2cb3c031beeec93cf660c8809d6ad58a2cde7dcd95ae12b4b2da6ac8e2da41", size = 1173429, upload_time = "2025-01-22T00:16:14.466Z" }, + { url = "https://files.pythonhosted.org/packages/3d/7c/d2a89b1c24c318375856e8b7611bc03ddf687134f68ddbb387496453eda8/pyopencl-2025.1-cp311-cp311-win_amd64.whl", hash = "sha256:d8d3cdb02dc8750a9cc11c5b37f219da1f61b4216af4a830eb52b451a0e9f9a7", size = 457877, upload_time = "2025-01-22T00:16:17.21Z" }, + { url = "https://files.pythonhosted.org/packages/02/c0/d9536211ecfddd3bdf7eaa1658d085fcd92120061ac6f4e662a5062660ff/pyopencl-2025.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:88c564d94a5067ab6b9429a7d92b655254da8b2b5a33c7e30e10c5a0cf3154e1", size = 425706, upload_time = "2025-01-22T00:16:18.771Z" }, + { url = "https://files.pythonhosted.org/packages/63/b9/3e6dd574cc9ffb2271be135ecdb36cd6aea70a1f74e80539b048072a256a/pyopencl-2025.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f204cd6974ca19e7ffe14f21afac9967b06a6718f3aecbbc4a4df313e8e7ebc2", size = 408163, upload_time = "2025-01-22T00:16:20.282Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4d/7f6f2e24b12585b81fd49f689d21ba62187656d061e3cb43840f12097dad/pyopencl-2025.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce8d1b3fd2046e89377b754117fdb3505f45edacfda6ad2b3a29670c0526ad1", size = 719348, upload_time = "2025-01-22T00:16:22.15Z" }, + { url = "https://files.pythonhosted.org/packages/f0/45/3c93510819859e047d494dd8f4ed80c26378bb964a8e5e850cc079cc1f6e/pyopencl-2025.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb0b7b775424f0c4c929a00f09eb351075ea9e4d2b1c80afe37d09bec218ee9", size = 1170733, upload_time = "2025-01-22T00:16:24.575Z" }, + { url = "https://files.pythonhosted.org/packages/91/ba/b745715085ef893fa7d1c7e04c95e3e554b6b27b2fb0800d6bbca563b43c/pyopencl-2025.1-cp312-cp312-win_amd64.whl", hash = "sha256:78f2a58d2e177793fb5a7b9c8a574e3a5f1d178c4df713969d1b08341c817d60", size = 457762, upload_time = "2025-01-22T00:16:27.334Z" }, ] [[package]] @@ -4270,37 +4277,37 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5d/70/ff56a63248562e77c0c8ee4aefc3224258f1856977e0c1472672b62dadb8/pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95", size = 184323 } +sdist = { url = "https://files.pythonhosted.org/packages/5d/70/ff56a63248562e77c0c8ee4aefc3224258f1856977e0c1472672b62dadb8/pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95", size = 184323, upload_time = "2024-07-20T17:26:31.252Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/dd/e0aa7ebef5168c75b772eda64978c597a9129b46be17779054652a7999e4/pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d", size = 58390 }, + { url = "https://files.pythonhosted.org/packages/d9/dd/e0aa7ebef5168c75b772eda64978c597a9129b46be17779054652a7999e4/pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d", size = 58390, upload_time = "2024-07-20T17:26:29.057Z" }, ] [[package]] name = "pyparsing" version = "3.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608 } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload_time = "2025-03-25T05:01:28.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120 }, + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload_time = "2025-03-25T05:01:24.908Z" }, ] [[package]] name = "pyperclip" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961 } +sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload_time = "2024-06-18T20:38:48.401Z" } [[package]] name = "pyprof2calltree" version = "1.4.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/2a/e9a76261183b4b5e059a6625d7aae0bcb0a77622bc767d4497148ce2e218/pyprof2calltree-1.4.5.tar.gz", hash = "sha256:a635672ff31677486350b2be9a823ef92f740e6354a6aeda8fa4a8a3768e8f2f", size = 10080 } +sdist = { url = "https://files.pythonhosted.org/packages/ca/2a/e9a76261183b4b5e059a6625d7aae0bcb0a77622bc767d4497148ce2e218/pyprof2calltree-1.4.5.tar.gz", hash = "sha256:a635672ff31677486350b2be9a823ef92f740e6354a6aeda8fa4a8a3768e8f2f", size = 10080, upload_time = "2020-04-19T10:39:09.819Z" } [[package]] name = "pyrect" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219 } +sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219, upload_time = "2022-03-16T04:45:52.36Z" } [[package]] name = "pyscreeze" @@ -4309,15 +4316,15 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pillow", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826, upload_time = "2024-08-20T23:03:07.291Z" } [[package]] name = "pyserial" version = "3.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125 } +sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload_time = "2020-11-23T03:59:15.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585 }, + { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload_time = "2020-11-23T03:59:13.41Z" }, ] [[package]] @@ -4330,9 +4337,9 @@ dependencies = [ { name = "packaging" }, { name = "pluggy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload_time = "2025-03-02T12:54:54.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload_time = "2025-03-02T12:54:52.069Z" }, ] [[package]] @@ -4342,9 +4349,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156 } +sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload_time = "2025-03-25T06:22:28.883Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694 }, + { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload_time = "2025-03-25T06:22:27.807Z" }, ] [[package]] @@ -4355,9 +4362,9 @@ dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857 } +sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload_time = "2025-04-05T14:07:51.592Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841 }, + { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload_time = "2025-04-05T14:07:49.641Z" }, ] [[package]] @@ -4367,9 +4374,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/a1/c2679d7ff2da20a0f89c7820ae2739cde739eac9b43c192531117b31b5f4/pytest_cpp-2.6.0.tar.gz", hash = "sha256:c2f49d3c038539ac84786a94d852e4f4619c34c95979c2bc69c20b3bdf051d85", size = 465490 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/a1/c2679d7ff2da20a0f89c7820ae2739cde739eac9b43c192531117b31b5f4/pytest_cpp-2.6.0.tar.gz", hash = "sha256:c2f49d3c038539ac84786a94d852e4f4619c34c95979c2bc69c20b3bdf051d85", size = 465490, upload_time = "2024-09-18T00:08:08.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/44/dc2f5d53165264ae5831f361fe7723c45da05718a97015b2eddc452cf503/pytest_cpp-2.6.0-py3-none-any.whl", hash = "sha256:b33de94609450feea2fba9efff3558b8ac8f1fdf40a99e263b395d4798b911bb", size = 15074 }, + { url = "https://files.pythonhosted.org/packages/2a/44/dc2f5d53165264ae5831f361fe7723c45da05718a97015b2eddc452cf503/pytest_cpp-2.6.0-py3-none-any.whl", hash = "sha256:b33de94609450feea2fba9efff3558b8ac8f1fdf40a99e263b395d4798b911bb", size = 15074, upload_time = "2024-09-18T00:08:06.415Z" }, ] [[package]] @@ -4379,9 +4386,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload_time = "2024-03-21T22:14:04.964Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload_time = "2024-03-21T22:14:02.694Z" }, ] [[package]] @@ -4391,9 +4398,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload_time = "2024-10-25T15:45:34.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396 }, + { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload_time = "2024-10-25T15:45:32.78Z" }, ] [[package]] @@ -4403,9 +4410,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/d4/69e9dbb9b8266df0b157c72be32083403c412990af15c7c15f7a3fd1b142/pytest_repeat-0.9.4.tar.gz", hash = "sha256:d92ac14dfaa6ffcfe6917e5d16f0c9bc82380c135b03c2a5f412d2637f224485", size = 6488 } +sdist = { url = "https://files.pythonhosted.org/packages/80/d4/69e9dbb9b8266df0b157c72be32083403c412990af15c7c15f7a3fd1b142/pytest_repeat-0.9.4.tar.gz", hash = "sha256:d92ac14dfaa6ffcfe6917e5d16f0c9bc82380c135b03c2a5f412d2637f224485", size = 6488, upload_time = "2025-04-07T14:59:53.077Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/d4/8b706b81b07b43081bd68a2c0359fe895b74bf664b20aca8005d2bb3be71/pytest_repeat-0.9.4-py3-none-any.whl", hash = "sha256:c1738b4e412a6f3b3b9e0b8b29fcd7a423e50f87381ad9307ef6f5a8601139f3", size = 4180 }, + { url = "https://files.pythonhosted.org/packages/73/d4/8b706b81b07b43081bd68a2c0359fe895b74bf664b20aca8005d2bb3be71/pytest_repeat-0.9.4-py3-none-any.whl", hash = "sha256:c1738b4e412a6f3b3b9e0b8b29fcd7a423e50f87381ad9307ef6f5a8601139f3", size = 4180, upload_time = "2025-04-07T14:59:51.492Z" }, ] [[package]] @@ -4416,9 +4423,9 @@ dependencies = [ { name = "attrs" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/4c/ba9eab21a2250c2d46c06c0e3cd316850fde9a90da0ac8d0202f074c6817/pytest_subtests-0.14.1.tar.gz", hash = "sha256:350c00adc36c3aff676a66135c81aed9e2182e15f6c3ec8721366918bbbf7580", size = 17632 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/4c/ba9eab21a2250c2d46c06c0e3cd316850fde9a90da0ac8d0202f074c6817/pytest_subtests-0.14.1.tar.gz", hash = "sha256:350c00adc36c3aff676a66135c81aed9e2182e15f6c3ec8721366918bbbf7580", size = 17632, upload_time = "2024-12-10T00:21:04.856Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b7/7ca948d35642ae72500efda6ba6fa61dcb6683feb596d19c4747c63c0789/pytest_subtests-0.14.1-py3-none-any.whl", hash = "sha256:e92a780d98b43118c28a16044ad9b841727bd7cb6a417073b38fd2d7ccdf052d", size = 8833 }, + { url = "https://files.pythonhosted.org/packages/a9/b7/7ca948d35642ae72500efda6ba6fa61dcb6683feb596d19c4747c63c0789/pytest_subtests-0.14.1-py3-none-any.whl", hash = "sha256:e92a780d98b43118c28a16044ad9b841727bd7cb6a417073b38fd2d7ccdf052d", size = 8833, upload_time = "2024-12-10T00:20:58.873Z" }, ] [[package]] @@ -4428,9 +4435,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/0d/04719abc7a4bdb3a7a1f968f24b0f5253d698c9cc94975330e9d3145befb/pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", size = 17697 } +sdist = { url = "https://files.pythonhosted.org/packages/93/0d/04719abc7a4bdb3a7a1f968f24b0f5253d698c9cc94975330e9d3145befb/pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", size = 17697, upload_time = "2024-03-07T21:04:01.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148 }, + { url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148, upload_time = "2024-03-07T21:03:58.764Z" }, ] [[package]] @@ -4441,9 +4448,9 @@ dependencies = [ { name = "execnet" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060 } +sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060, upload_time = "2024-04-28T19:29:54.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108 }, + { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108, upload_time = "2024-04-28T19:29:52.813Z" }, ] [[package]] @@ -4453,9 +4460,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload_time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload_time = "2024-03-01T18:36:18.57Z" }, ] [[package]] @@ -4465,16 +4472,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068 } +sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload_time = "2022-12-25T18:53:00.824Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185 }, + { url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185, upload_time = "2022-12-25T18:52:58.662Z" }, ] [[package]] name = "python3-xlib" version = "0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828 } +sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828, upload_time = "2014-05-31T12:28:59.603Z" } [[package]] name = "pytools" @@ -4485,28 +4492,28 @@ dependencies = [ { name = "siphash24" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741 } +sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload_time = "2024-07-17T18:47:38.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/cf/0a6aaa44b1f9e02b8c0648b5665a82246a93bcc75224c167b4fafa25c093/pytools-2024.1.10-py3-none-any.whl", hash = "sha256:9cabb71038048291400e244e2da441a051d86053339bc484e64e58d8ea263f44", size = 88108 }, + { url = "https://files.pythonhosted.org/packages/66/cf/0a6aaa44b1f9e02b8c0648b5665a82246a93bcc75224c167b4fafa25c093/pytools-2024.1.10-py3-none-any.whl", hash = "sha256:9cabb71038048291400e244e2da441a051d86053339bc484e64e58d8ea263f44", size = 88108, upload_time = "2024-07-17T18:47:36.173Z" }, ] [[package]] name = "pytweening" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241 } +sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241, upload_time = "2024-02-20T03:37:56.809Z" } [[package]] name = "pywin32" version = "310" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284 }, - { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748 }, - { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941 }, - { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239 }, - { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839 }, - { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470 }, + { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284, upload_time = "2025-03-17T00:55:53.124Z" }, + { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748, upload_time = "2025-03-17T00:55:55.203Z" }, + { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941, upload_time = "2025-03-17T00:55:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload_time = "2025-03-17T00:55:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload_time = "2025-03-17T00:56:00.8Z" }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload_time = "2025-03-17T00:56:02.601Z" }, ] [[package]] @@ -4521,7 +4528,7 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/37/d59397221e15d2a7f38afaa4e8e6b8c244d818044f7daa0bdc5988df0a69/PyWinBox-0.7-py3-none-any.whl", hash = "sha256:8b2506a8dd7afa0a910b368762adfac885274132ef9151b0c81b0d2c6ffd6f83", size = 12274 }, + { url = "https://files.pythonhosted.org/packages/b1/37/d59397221e15d2a7f38afaa4e8e6b8c244d818044f7daa0bdc5988df0a69/PyWinBox-0.7-py3-none-any.whl", hash = "sha256:8b2506a8dd7afa0a910b368762adfac885274132ef9151b0c81b0d2c6ffd6f83", size = 12274, upload_time = "2024-04-17T10:10:31.899Z" }, ] [[package]] @@ -4538,33 +4545,33 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/be/33/8e4f632210b28fc9e998a9ab990e7ed97ecd2800cc50038e3800e1d85dbe/PyWinCtl-0.4.1-py3-none-any.whl", hash = "sha256:4d875e22969e1c6239d8c73156193630aaab876366167b8d97716f956384b089", size = 63158 }, + { url = "https://files.pythonhosted.org/packages/be/33/8e4f632210b28fc9e998a9ab990e7ed97ecd2800cc50038e3800e1d85dbe/PyWinCtl-0.4.1-py3-none-any.whl", hash = "sha256:4d875e22969e1c6239d8c73156193630aaab876366167b8d97716f956384b089", size = 63158, upload_time = "2024-09-23T08:33:39.881Z" }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload_time = "2024-08-06T20:33:50.674Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/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 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload_time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload_time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload_time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload_time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload_time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload_time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload_time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload_time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload_time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload_time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload_time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload_time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload_time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload_time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload_time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload_time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload_time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload_time = "2024-08-06T20:32:41.93Z" }, ] [[package]] @@ -4574,9 +4581,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631, upload_time = "2020-11-12T02:38:26.239Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911, upload_time = "2020-11-12T02:38:24.638Z" }, ] [[package]] @@ -4586,35 +4593,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293 } +sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293, upload_time = "2025-04-04T12:05:44.049Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723 }, - { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645 }, - { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133 }, - { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428 }, - { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409 }, - { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007 }, - { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599 }, - { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546 }, - { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247 }, - { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727 }, - { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942 }, - { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586 }, - { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880 }, - { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216 }, - { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814 }, - { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889 }, - { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153 }, - { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352 }, - { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834 }, - { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992 }, - { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466 }, - { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342 }, - { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405 }, - { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578 }, - { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248 }, - { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757 }, - { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371 }, + { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723, upload_time = "2025-04-04T12:03:24.358Z" }, + { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645, upload_time = "2025-04-04T12:03:25.693Z" }, + { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133, upload_time = "2025-04-04T12:03:27.625Z" }, + { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428, upload_time = "2025-04-04T12:03:29.004Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409, upload_time = "2025-04-04T12:03:31.032Z" }, + { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007, upload_time = "2025-04-04T12:03:32.687Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599, upload_time = "2025-04-04T12:03:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546, upload_time = "2025-04-04T12:03:35.478Z" }, + { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247, upload_time = "2025-04-04T12:03:36.846Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727, upload_time = "2025-04-04T12:03:38.578Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942, upload_time = "2025-04-04T12:03:40.143Z" }, + { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586, upload_time = "2025-04-04T12:03:41.954Z" }, + { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880, upload_time = "2025-04-04T12:03:43.45Z" }, + { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216, upload_time = "2025-04-04T12:03:45.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814, upload_time = "2025-04-04T12:03:47.188Z" }, + { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889, upload_time = "2025-04-04T12:03:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153, upload_time = "2025-04-04T12:03:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352, upload_time = "2025-04-04T12:03:52.473Z" }, + { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834, upload_time = "2025-04-04T12:03:54Z" }, + { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992, upload_time = "2025-04-04T12:03:55.815Z" }, + { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466, upload_time = "2025-04-04T12:03:57.231Z" }, + { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342, upload_time = "2025-04-04T12:03:59.218Z" }, + { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405, upload_time = "2025-04-04T12:05:13.3Z" }, + { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578, upload_time = "2025-04-04T12:05:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248, upload_time = "2025-04-04T12:05:17.376Z" }, + { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757, upload_time = "2025-04-04T12:05:19.19Z" }, + { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371, upload_time = "2025-04-04T12:05:20.702Z" }, ] [[package]] @@ -4624,21 +4631,21 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/35/9bf3a2af73c55fd4310dcaec4f997c739888e0db9b4dfac71b7680810852/raylib-5.5.0.2.tar.gz", hash = "sha256:83c108ae3b4af40b53c93d1de2afbe309e986dd5efeb280ebe2e61c79956edb0", size = 181172 } +sdist = { url = "https://files.pythonhosted.org/packages/8c/35/9bf3a2af73c55fd4310dcaec4f997c739888e0db9b4dfac71b7680810852/raylib-5.5.0.2.tar.gz", hash = "sha256:83c108ae3b4af40b53c93d1de2afbe309e986dd5efeb280ebe2e61c79956edb0", size = 181172, upload_time = "2024-11-26T11:12:02.791Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c4/ce21721b474eb8f65379f7315b382ccfe1d5df728eea4dcf287b874e7461/raylib-5.5.0.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:37eb0ec97fc6b08f989489a50e09b5dde519e1bb8eb17e4033ac82227b0e5eda", size = 1703742 }, - { url = "https://files.pythonhosted.org/packages/23/61/138e305c82549869bb8cd41abe75571559eafbeab6aed1ce7d8fbe3ffd58/raylib-5.5.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bb9e506ecd3dbec6dba868eb036269837a8bde68220690842c3238239ee887ef", size = 1247449 }, - { url = "https://files.pythonhosted.org/packages/85/e0/dc638c42d1a505f0992263d48e1434d82c21afdf376b06f549d2e281dfd4/raylib-5.5.0.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:70aa8bed67875a8cf25191f35263ef92d646bdfcb1f507915c81562a321f4931", size = 2184315 }, - { url = "https://files.pythonhosted.org/packages/c9/1a/49db57283a28fdc1ff0e4604911b7fff085128c2ac8bdd9efa8c5c47439d/raylib-5.5.0.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:0365e8c578f72f598795d9377fc70342f0d62aa193c2f304ca048b3e28866752", size = 2278139 }, - { url = "https://files.pythonhosted.org/packages/f0/8a/e1a690ab6889d4cb67346a2d32bad8b8e8b0f85ec826b00f76b0ad7e6ad6/raylib-5.5.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:5219be70e7fca03e9c4fddebf7e60e885d77137125c7a13f3800a947f8562a13", size = 1693944 }, - { url = "https://files.pythonhosted.org/packages/69/2b/49bfa6833ad74ddf318d54ecafe73d535f583531469ecbd5b009d79667d1/raylib-5.5.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5233c529d9a0cfd469d88239c2182e55c5215a7755d83cc3d611148d3b9c9e67", size = 1706157 }, - { url = "https://files.pythonhosted.org/packages/58/9c/8a3f4de0c81ad1228bf26410cfe3ecdc73011c59f18e542685ffc92c0120/raylib-5.5.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1f76204ffbc492722b571b12dbdc0dca89b10da76ddf48c12a3968d2db061dff", size = 1248027 }, - { url = "https://files.pythonhosted.org/packages/7f/16/63baf1aae94832b9f5d15cafcee67bb6dd07a20cf64d40bac09903b79274/raylib-5.5.0.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:f8cc2e39f1d6b29211a97ec0ac818a5b04c43a40e747e4b4622101d48c711f9e", size = 2195374 }, - { url = "https://files.pythonhosted.org/packages/70/bd/61a006b4e3ce4a6ca974cb0ceeb19f3816815ebabac650e9bf82767e65f6/raylib-5.5.0.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f12da578a28da7f48481f46323e5aab8dd25461982b0e80d045782d6e69649f5", size = 2299593 }, - { url = "https://files.pythonhosted.org/packages/f4/4f/59d554cc495bea8235b17cebfc76ed57aaa602c613b870159e31282fd4c1/raylib-5.5.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:b40234bbad9523fd6a2049640c76a98b4d6f0b8f4bd19bd33eaee55faf5e050d", size = 1696780 }, - { url = "https://files.pythonhosted.org/packages/4a/22/2e02e3738ad041f5ec2830aecdfab411fc2960bfc3400e03b477284bfaf7/raylib-5.5.0.2-pp311-pypy311_pp73-macosx_10_13_x86_64.whl", hash = "sha256:bc45fe1c0aac50aa319a9a66d44bb2bd0dcd038a44d95978191ae7bfeb4a06d8", size = 1216231 }, - { url = "https://files.pythonhosted.org/packages/fe/7d/b29afedc4a706b12143f74f322cb32ad5a6f43e56aaca2a9fb89b0d94eee/raylib-5.5.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.whl", hash = "sha256:2242fd6079da5137e9863a447224f800adef6386ca8f59013a5d62cc5cadab2b", size = 1394928 }, - { url = "https://files.pythonhosted.org/packages/b6/fa/2daf36d78078c6871b241168a36156169cfc8ea089faba5abe8edad304be/raylib-5.5.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e475a40764c9f83f9e66406bd86d85587eb923329a61ade463c3c59e1e880b16", size = 1564224 }, + { url = "https://files.pythonhosted.org/packages/9e/c4/ce21721b474eb8f65379f7315b382ccfe1d5df728eea4dcf287b874e7461/raylib-5.5.0.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:37eb0ec97fc6b08f989489a50e09b5dde519e1bb8eb17e4033ac82227b0e5eda", size = 1703742, upload_time = "2024-11-26T11:09:31.115Z" }, + { url = "https://files.pythonhosted.org/packages/23/61/138e305c82549869bb8cd41abe75571559eafbeab6aed1ce7d8fbe3ffd58/raylib-5.5.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bb9e506ecd3dbec6dba868eb036269837a8bde68220690842c3238239ee887ef", size = 1247449, upload_time = "2024-11-26T11:09:34.182Z" }, + { url = "https://files.pythonhosted.org/packages/85/e0/dc638c42d1a505f0992263d48e1434d82c21afdf376b06f549d2e281dfd4/raylib-5.5.0.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:70aa8bed67875a8cf25191f35263ef92d646bdfcb1f507915c81562a321f4931", size = 2184315, upload_time = "2024-11-26T11:09:36.715Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1a/49db57283a28fdc1ff0e4604911b7fff085128c2ac8bdd9efa8c5c47439d/raylib-5.5.0.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:0365e8c578f72f598795d9377fc70342f0d62aa193c2f304ca048b3e28866752", size = 2278139, upload_time = "2024-11-26T11:09:39.475Z" }, + { url = "https://files.pythonhosted.org/packages/f0/8a/e1a690ab6889d4cb67346a2d32bad8b8e8b0f85ec826b00f76b0ad7e6ad6/raylib-5.5.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:5219be70e7fca03e9c4fddebf7e60e885d77137125c7a13f3800a947f8562a13", size = 1693944, upload_time = "2024-11-26T11:09:41.596Z" }, + { url = "https://files.pythonhosted.org/packages/69/2b/49bfa6833ad74ddf318d54ecafe73d535f583531469ecbd5b009d79667d1/raylib-5.5.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5233c529d9a0cfd469d88239c2182e55c5215a7755d83cc3d611148d3b9c9e67", size = 1706157, upload_time = "2024-11-26T11:09:43.6Z" }, + { url = "https://files.pythonhosted.org/packages/58/9c/8a3f4de0c81ad1228bf26410cfe3ecdc73011c59f18e542685ffc92c0120/raylib-5.5.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1f76204ffbc492722b571b12dbdc0dca89b10da76ddf48c12a3968d2db061dff", size = 1248027, upload_time = "2025-01-04T20:21:46.269Z" }, + { url = "https://files.pythonhosted.org/packages/7f/16/63baf1aae94832b9f5d15cafcee67bb6dd07a20cf64d40bac09903b79274/raylib-5.5.0.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:f8cc2e39f1d6b29211a97ec0ac818a5b04c43a40e747e4b4622101d48c711f9e", size = 2195374, upload_time = "2024-11-26T11:09:46.114Z" }, + { url = "https://files.pythonhosted.org/packages/70/bd/61a006b4e3ce4a6ca974cb0ceeb19f3816815ebabac650e9bf82767e65f6/raylib-5.5.0.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f12da578a28da7f48481f46323e5aab8dd25461982b0e80d045782d6e69649f5", size = 2299593, upload_time = "2024-11-26T11:09:48.963Z" }, + { url = "https://files.pythonhosted.org/packages/f4/4f/59d554cc495bea8235b17cebfc76ed57aaa602c613b870159e31282fd4c1/raylib-5.5.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:b40234bbad9523fd6a2049640c76a98b4d6f0b8f4bd19bd33eaee55faf5e050d", size = 1696780, upload_time = "2024-11-26T11:09:50.787Z" }, + { url = "https://files.pythonhosted.org/packages/4a/22/2e02e3738ad041f5ec2830aecdfab411fc2960bfc3400e03b477284bfaf7/raylib-5.5.0.2-pp311-pypy311_pp73-macosx_10_13_x86_64.whl", hash = "sha256:bc45fe1c0aac50aa319a9a66d44bb2bd0dcd038a44d95978191ae7bfeb4a06d8", size = 1216231, upload_time = "2025-02-12T04:21:59.38Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7d/b29afedc4a706b12143f74f322cb32ad5a6f43e56aaca2a9fb89b0d94eee/raylib-5.5.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.whl", hash = "sha256:2242fd6079da5137e9863a447224f800adef6386ca8f59013a5d62cc5cadab2b", size = 1394928, upload_time = "2025-02-12T04:22:03.021Z" }, + { url = "https://files.pythonhosted.org/packages/b6/fa/2daf36d78078c6871b241168a36156169cfc8ea089faba5abe8edad304be/raylib-5.5.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e475a40764c9f83f9e66406bd86d85587eb923329a61ade463c3c59e1e880b16", size = 1564224, upload_time = "2025-02-12T04:22:05.911Z" }, ] [[package]] @@ -4651,14 +4658,14 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload_time = "2024-05-29T15:37:49.536Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload_time = "2024-05-29T15:37:47.027Z" }, ] [[package]] name = "rerun-sdk" -version = "0.22.1" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -4668,11 +4675,11 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/41/f9724c6ee4b4d45f9ae433f41fc8b4f2827d5b3f9459b9e9a9c55fdda5ab/rerun_sdk-0.22.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:176853d14bcac0b3cab8b240fe6760e32a577b1edd9ecb2fd0656e83f046a2c4", size = 46819325 }, - { url = "https://files.pythonhosted.org/packages/01/8e/db9fd7e7d9d9ec6e3f0781ce6f3b71c06952f9b0c40e9720822fd83f711e/rerun_sdk-0.22.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:dea4a50c916bc82bd97a8f9dc44c71f9a9fccec7e73f37edfaa0de800caf5dce", size = 44566672 }, - { url = "https://files.pythonhosted.org/packages/ad/41/87079d0a897e4fc2dd54a356be9556626f40ec1ff1c3393178e7ac9feb9d/rerun_sdk-0.22.1-cp38-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:29d807909f5e484aa6427d9fb25706d0a154de392a4ca47eb303fbd135c0e382", size = 49830061 }, - { url = "https://files.pythonhosted.org/packages/1e/db/3ce2be017d7d4ac0948fb064bf7417b36c96bc07d1b8a922017606ba03c6/rerun_sdk-0.22.1-cp38-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:b9672412d0cdf57c79c10ca683e59716399da3358df78b985b25093d022ebbaf", size = 51423327 }, - { url = "https://files.pythonhosted.org/packages/43/eb/edafdcba1619955c2c9b66c7a1bcbbce4ad62a1a36db984b46aa793fe366/rerun_sdk-0.22.1-cp38-abi3-win_amd64.whl", hash = "sha256:d3d0afe1b8e749a1088a0bb75f372ad7fe70eb3728bd32e07bf78300436a053d", size = 42085149 }, + { url = "https://files.pythonhosted.org/packages/dd/6e/a125f4fe2de3269f443b7cb65d465ffd37a836a2dac7e4318e21239d78c8/rerun_sdk-0.23.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fe06d21cfcf4d84a9396f421d4779efabec7e9674d232a2c552c8a91d871c375", size = 66094053, upload_time = "2025-04-25T13:15:48.669Z" }, + { url = "https://files.pythonhosted.org/packages/55/f6/b6d13322b05dc77bd9a0127e98155c2b7ee987a236fd4d331eed2e547a90/rerun_sdk-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:823ae87bfa644e06fb70bada08a83690dd23d9824a013947f80a22c6731bdc0d", size = 62047843, upload_time = "2025-04-25T13:15:54.48Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7f/6a7422cb727e14a65b55b0089988eeea8d0532c429397a863e6ba395554a/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:dc5129f8744f71249bf45558c853422c51ef39b6b5eea0ea1f602c6049ce732f", size = 68214509, upload_time = "2025-04-25T13:15:59.339Z" }, + { url = "https://files.pythonhosted.org/packages/4f/86/3aee9eadbfe55188a2c7d739378545b4319772a4d3b165e8d3fc598fa630/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:ee0d0e17df0e08be13b77cc74884c5d8ba8edb39b6f5a60dc2429d39033d90f6", size = 71442196, upload_time = "2025-04-25T13:16:04.405Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ba/028bd382e2ae21e6643cec25f423285dbc6b328ce56d55727b4101ef9443/rerun_sdk-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:d4273db55b56310b053a2de6bf5927a8692cf65f4d234c6e6928fb24ed8a960d", size = 57583198, upload_time = "2025-04-25T13:16:08.905Z" }, ] [[package]] @@ -4682,132 +4689,132 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447 } +sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447, upload_time = "2025-01-06T14:08:51.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729 }, + { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729, upload_time = "2025-01-06T14:08:47.471Z" }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315 } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload_time = "2024-10-20T10:10:56.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224 }, - { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480 }, - { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068 }, - { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012 }, - { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352 }, - { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344 }, - { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498 }, - { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205 }, - { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185 }, - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433 }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362 }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118 }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497 }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042 }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831 }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692 }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777 }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523 }, + { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload_time = "2024-10-20T10:12:45.162Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload_time = "2024-10-20T10:12:46.758Z" }, + { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload_time = "2024-10-20T10:12:48.605Z" }, + { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload_time = "2024-10-20T10:12:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload_time = "2024-10-21T11:26:41.438Z" }, + { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload_time = "2024-10-21T11:26:43.62Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload_time = "2024-12-11T19:58:15.592Z" }, + { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload_time = "2024-10-20T10:12:52.865Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload_time = "2024-10-20T10:12:54.652Z" }, + { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload_time = "2024-10-20T10:12:55.657Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload_time = "2024-10-20T10:12:57.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload_time = "2024-10-20T10:12:58.501Z" }, + { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload_time = "2024-10-20T10:13:00.211Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload_time = "2024-10-21T11:26:46.038Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload_time = "2024-10-21T11:26:47.487Z" }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload_time = "2024-12-11T19:58:17.252Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload_time = "2024-10-20T10:13:01.395Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload_time = "2024-10-20T10:13:02.768Z" }, ] [[package]] name = "rubicon-objc" version = "0.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/13/586c9baa985eae0f718029506b40ca41295d51a546567414b2bcf8ccacef/rubicon_objc-0.5.0.tar.gz", hash = "sha256:18f075649780d95df53d483642068c767d7d2cfbbf075ddef124a44b40b6d92e", size = 173652 } +sdist = { url = "https://files.pythonhosted.org/packages/d3/13/586c9baa985eae0f718029506b40ca41295d51a546567414b2bcf8ccacef/rubicon_objc-0.5.0.tar.gz", hash = "sha256:18f075649780d95df53d483642068c767d7d2cfbbf075ddef124a44b40b6d92e", size = 173652, upload_time = "2025-01-07T00:25:10.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/30/5b2407b8762ed882e5732e19c485b9ea2f07d35462615a3212638bab66c2/rubicon_objc-0.5.0-py3-none-any.whl", hash = "sha256:a9c2a605120d6e5be327d3f42a71b60963125987e116f51846757b5e110854fa", size = 62711 }, + { url = "https://files.pythonhosted.org/packages/6d/30/5b2407b8762ed882e5732e19c485b9ea2f07d35462615a3212638bab66c2/rubicon_objc-0.5.0-py3-none-any.whl", hash = "sha256:a9c2a605120d6e5be327d3f42a71b60963125987e116f51846757b5e110854fa", size = 62711, upload_time = "2025-01-07T00:25:08.959Z" }, ] [[package]] name = "ruff" -version = "0.11.5" +version = "0.11.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/71/5759b2a6b2279bb77fe15b1435b89473631c2cd6374d45ccdb6b785810be/ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef", size = 3976488 } +sdist = { url = "https://files.pythonhosted.org/packages/5b/89/6f9c9674818ac2e9cc2f2b35b704b7768656e6b7c139064fc7ba8fbc99f1/ruff-0.11.7.tar.gz", hash = "sha256:655089ad3224070736dc32844fde783454f8558e71f501cb207485fe4eee23d4", size = 4054861, upload_time = "2025-04-24T18:49:37.007Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/db/6efda6381778eec7f35875b5cbefd194904832a1153d68d36d6b269d81a8/ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b", size = 10103150 }, - { url = "https://files.pythonhosted.org/packages/44/f2/06cd9006077a8db61956768bc200a8e52515bf33a8f9b671ee527bb10d77/ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077", size = 10898637 }, - { url = "https://files.pythonhosted.org/packages/18/f5/af390a013c56022fe6f72b95c86eb7b2585c89cc25d63882d3bfe411ecf1/ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779", size = 10236012 }, - { url = "https://files.pythonhosted.org/packages/b8/ca/b9bf954cfed165e1a0c24b86305d5c8ea75def256707f2448439ac5e0d8b/ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794", size = 10415338 }, - { url = "https://files.pythonhosted.org/packages/d9/4d/2522dde4e790f1b59885283f8786ab0046958dfd39959c81acc75d347467/ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038", size = 9965277 }, - { url = "https://files.pythonhosted.org/packages/e5/7a/749f56f150eef71ce2f626a2f6988446c620af2f9ba2a7804295ca450397/ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f", size = 11541614 }, - { url = "https://files.pythonhosted.org/packages/89/b2/7d9b8435222485b6aac627d9c29793ba89be40b5de11584ca604b829e960/ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82", size = 12198873 }, - { url = "https://files.pythonhosted.org/packages/00/e0/a1a69ef5ffb5c5f9c31554b27e030a9c468fc6f57055886d27d316dfbabd/ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304", size = 11670190 }, - { url = "https://files.pythonhosted.org/packages/05/61/c1c16df6e92975072c07f8b20dad35cd858e8462b8865bc856fe5d6ccb63/ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470", size = 13902301 }, - { url = "https://files.pythonhosted.org/packages/79/89/0af10c8af4363304fd8cb833bd407a2850c760b71edf742c18d5a87bb3ad/ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a", size = 11350132 }, - { url = "https://files.pythonhosted.org/packages/b9/e1/ecb4c687cbf15164dd00e38cf62cbab238cad05dd8b6b0fc68b0c2785e15/ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b", size = 10312937 }, - { url = "https://files.pythonhosted.org/packages/cf/4f/0e53fe5e500b65934500949361e3cd290c5ba60f0324ed59d15f46479c06/ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a", size = 9936683 }, - { url = "https://files.pythonhosted.org/packages/04/a8/8183c4da6d35794ae7f76f96261ef5960853cd3f899c2671961f97a27d8e/ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159", size = 10950217 }, - { url = "https://files.pythonhosted.org/packages/26/88/9b85a5a8af21e46a0639b107fcf9bfc31da4f1d263f2fc7fbe7199b47f0a/ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783", size = 11404521 }, - { url = "https://files.pythonhosted.org/packages/fc/52/047f35d3b20fd1ae9ccfe28791ef0f3ca0ef0b3e6c1a58badd97d450131b/ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe", size = 10320697 }, - { url = "https://files.pythonhosted.org/packages/b9/fe/00c78010e3332a6e92762424cf4c1919065707e962232797d0b57fd8267e/ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800", size = 11378665 }, - { url = "https://files.pythonhosted.org/packages/43/7c/c83fe5cbb70ff017612ff36654edfebec4b1ef79b558b8e5fd933bab836b/ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e", size = 10460287 }, + { url = "https://files.pythonhosted.org/packages/b4/ec/21927cb906c5614b786d1621dba405e3d44f6e473872e6df5d1a6bca0455/ruff-0.11.7-py3-none-linux_armv6l.whl", hash = "sha256:d29e909d9a8d02f928d72ab7837b5cbc450a5bdf578ab9ebee3263d0a525091c", size = 10245403, upload_time = "2025-04-24T18:48:40.459Z" }, + { url = "https://files.pythonhosted.org/packages/e2/af/fec85b6c2c725bcb062a354dd7cbc1eed53c33ff3aa665165871c9c16ddf/ruff-0.11.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dd1fb86b168ae349fb01dd497d83537b2c5541fe0626e70c786427dd8363aaee", size = 11007166, upload_time = "2025-04-24T18:48:44.742Z" }, + { url = "https://files.pythonhosted.org/packages/31/9a/2d0d260a58e81f388800343a45898fd8df73c608b8261c370058b675319a/ruff-0.11.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d3d7d2e140a6fbbc09033bce65bd7ea29d6a0adeb90b8430262fbacd58c38ada", size = 10378076, upload_time = "2025-04-24T18:48:47.918Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/9b09b45051404d2e7dd6d9dbcbabaa5ab0093f9febcae664876a77b9ad53/ruff-0.11.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4809df77de390a1c2077d9b7945d82f44b95d19ceccf0c287c56e4dc9b91ca64", size = 10557138, upload_time = "2025-04-24T18:48:51.707Z" }, + { url = "https://files.pythonhosted.org/packages/5e/5e/f62a1b6669870a591ed7db771c332fabb30f83c967f376b05e7c91bccd14/ruff-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3a0c2e169e6b545f8e2dba185eabbd9db4f08880032e75aa0e285a6d3f48201", size = 10095726, upload_time = "2025-04-24T18:48:54.243Z" }, + { url = "https://files.pythonhosted.org/packages/45/59/a7aa8e716f4cbe07c3500a391e58c52caf665bb242bf8be42c62adef649c/ruff-0.11.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49b888200a320dd96a68e86736cf531d6afba03e4f6cf098401406a257fcf3d6", size = 11672265, upload_time = "2025-04-24T18:48:57.639Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e3/101a8b707481f37aca5f0fcc3e42932fa38b51add87bfbd8e41ab14adb24/ruff-0.11.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2b19cdb9cf7dae00d5ee2e7c013540cdc3b31c4f281f1dacb5a799d610e90db4", size = 12331418, upload_time = "2025-04-24T18:49:00.697Z" }, + { url = "https://files.pythonhosted.org/packages/dd/71/037f76cbe712f5cbc7b852e4916cd3cf32301a30351818d32ab71580d1c0/ruff-0.11.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64e0ee994c9e326b43539d133a36a455dbaab477bc84fe7bfbd528abe2f05c1e", size = 11794506, upload_time = "2025-04-24T18:49:03.545Z" }, + { url = "https://files.pythonhosted.org/packages/ca/de/e450b6bab1fc60ef263ef8fcda077fb4977601184877dce1c59109356084/ruff-0.11.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bad82052311479a5865f52c76ecee5d468a58ba44fb23ee15079f17dd4c8fd63", size = 13939084, upload_time = "2025-04-24T18:49:07.159Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2c/1e364cc92970075d7d04c69c928430b23e43a433f044474f57e425cbed37/ruff-0.11.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7940665e74e7b65d427b82bffc1e46710ec7f30d58b4b2d5016e3f0321436502", size = 11450441, upload_time = "2025-04-24T18:49:11.41Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7d/1b048eb460517ff9accd78bca0fa6ae61df2b276010538e586f834f5e402/ruff-0.11.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:169027e31c52c0e36c44ae9a9c7db35e505fee0b39f8d9fca7274a6305295a92", size = 10441060, upload_time = "2025-04-24T18:49:14.184Z" }, + { url = "https://files.pythonhosted.org/packages/3a/57/8dc6ccfd8380e5ca3d13ff7591e8ba46a3b330323515a4996b991b10bd5d/ruff-0.11.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:305b93f9798aee582e91e34437810439acb28b5fc1fee6b8205c78c806845a94", size = 10058689, upload_time = "2025-04-24T18:49:17.559Z" }, + { url = "https://files.pythonhosted.org/packages/23/bf/20487561ed72654147817885559ba2aa705272d8b5dee7654d3ef2dbf912/ruff-0.11.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a681db041ef55550c371f9cd52a3cf17a0da4c75d6bd691092dfc38170ebc4b6", size = 11073703, upload_time = "2025-04-24T18:49:20.247Z" }, + { url = "https://files.pythonhosted.org/packages/9d/27/04f2db95f4ef73dccedd0c21daf9991cc3b7f29901a4362057b132075aa4/ruff-0.11.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:07f1496ad00a4a139f4de220b0c97da6d4c85e0e4aa9b2624167b7d4d44fd6b6", size = 11532822, upload_time = "2025-04-24T18:49:23.765Z" }, + { url = "https://files.pythonhosted.org/packages/e1/72/43b123e4db52144c8add336581de52185097545981ff6e9e58a21861c250/ruff-0.11.7-py3-none-win32.whl", hash = "sha256:f25dfb853ad217e6e5f1924ae8a5b3f6709051a13e9dad18690de6c8ff299e26", size = 10362436, upload_time = "2025-04-24T18:49:27.377Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a0/3e58cd76fdee53d5c8ce7a56d84540833f924ccdf2c7d657cb009e604d82/ruff-0.11.7-py3-none-win_amd64.whl", hash = "sha256:0a931d85959ceb77e92aea4bbedfded0a31534ce191252721128f77e5ae1f98a", size = 11566676, upload_time = "2025-04-24T18:49:30.938Z" }, + { url = "https://files.pythonhosted.org/packages/68/ca/69d7c7752bce162d1516e5592b1cc6b6668e9328c0d270609ddbeeadd7cf/ruff-0.11.7-py3-none-win_arm64.whl", hash = "sha256:778c1e5d6f9e91034142dfd06110534ca13220bfaad5c3735f6cb844654f6177", size = 10677936, upload_time = "2025-04-24T18:49:34.392Z" }, ] [[package]] name = "scons" version = "4.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/c1/30176c76c1ef723fab62e5cdb15d3c972427a146cb6f868748613d7b25af/scons-4.9.1.tar.gz", hash = "sha256:bacac880ba2e86d6a156c116e2f8f2bfa82b257046f3ac2666c85c53c615c338", size = 3252106 } +sdist = { url = "https://files.pythonhosted.org/packages/c8/c1/30176c76c1ef723fab62e5cdb15d3c972427a146cb6f868748613d7b25af/scons-4.9.1.tar.gz", hash = "sha256:bacac880ba2e86d6a156c116e2f8f2bfa82b257046f3ac2666c85c53c615c338", size = 3252106, upload_time = "2025-03-27T20:42:19.765Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/92/50b739021983b131dcacd57aa8b04d31c5acc2e7e0eb4ed4a362f438c6b7/scons-4.9.1-py3-none-any.whl", hash = "sha256:d2db1a22eb6039e97cbbb0106f18f435af033d0aad190299c9688378e2810a5e", size = 4131331 }, + { url = "https://files.pythonhosted.org/packages/45/92/50b739021983b131dcacd57aa8b04d31c5acc2e7e0eb4ed4a362f438c6b7/scons-4.9.1-py3-none-any.whl", hash = "sha256:d2db1a22eb6039e97cbbb0106f18f435af033d0aad190299c9688378e2810a5e", size = 4131331, upload_time = "2025-03-27T20:42:16.665Z" }, ] [[package]] name = "sentry-sdk" -version = "2.26.0" +version = "2.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/e2/f8ebb2086046d89cb692f12cc7f9d84e02669d19e263f9b0347553ac6e8c/sentry_sdk-2.26.0.tar.gz", hash = "sha256:88643459716dd0c6e412e5141fcc94ce3b5725e4b6b312210b91332b3b46a0e2", size = 322738 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556, upload_time = "2025-04-24T10:09:37.927Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/da/195136b1113e5bc34f31ba04a345bc15c1e66a4eacfc1b317de111abe8c5/sentry_sdk-2.26.0-py2.py3-none-any.whl", hash = "sha256:82496fc359296dac57ec923300b18cc1f14a1279c1e7108d46d35dbb4cf8f5f8", size = 340184 }, + { url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786, upload_time = "2025-04-24T10:09:35.897Z" }, ] [[package]] name = "setproctitle" version = "1.3.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c4/4d/6a840c8d2baa07b57329490e7094f90aac177a1d5226bc919046f1106860/setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e", size = 26737 } +sdist = { url = "https://files.pythonhosted.org/packages/c4/4d/6a840c8d2baa07b57329490e7094f90aac177a1d5226bc919046f1106860/setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e", size = 26737, upload_time = "2025-02-22T21:52:43.276Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/4a/9e0243c5df221102fb834a947f5753d9da06ad5f84e36b0e2e93f7865edb/setproctitle-1.3.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8dcc250872385f2780a5ea58050b58cbc8b6a7e8444952a5a65c359886c593", size = 17256 }, - { url = "https://files.pythonhosted.org/packages/c7/a1/76ad2ba6f5bd00609238e3d64eeded4598e742a5f25b5cc1a0efdae5f674/setproctitle-1.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ca82fae9eb4800231dd20229f06e8919787135a5581da245b8b05e864f34cc8b", size = 11893 }, - { url = "https://files.pythonhosted.org/packages/47/3a/75d11fedff5b21ba9a4c5fe3dfa5e596f831d094ef1896713a72e9e38833/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0424e1d33232322541cb36fb279ea5242203cd6f20de7b4fb2a11973d8e8c2ce", size = 31631 }, - { url = "https://files.pythonhosted.org/packages/5a/12/58220de5600e0ed2e5562297173187d863db49babb03491ffe9c101299bc/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fec8340ab543144d04a9d805d80a0aad73fdeb54bea6ff94e70d39a676ea4ec0", size = 32975 }, - { url = "https://files.pythonhosted.org/packages/fa/c4/fbb308680d83c1c7aa626950308318c6e6381a8273779163a31741f3c752/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eab441c89f181271ab749077dcc94045a423e51f2fb0b120a1463ef9820a08d0", size = 30126 }, - { url = "https://files.pythonhosted.org/packages/31/6e/baaf70bd9a881dd8c12cbccdd7ca0ff291024a37044a8245e942e12e7135/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c371550a2288901a0dcd84192691ebd3197a43c95f3e0b396ed6d1cedf5c6c", size = 31135 }, - { url = "https://files.pythonhosted.org/packages/a6/dc/d8ab6b1c3d844dc14f596e3cce76604570848f8a67ba6a3812775ed2c015/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78288ff5f9c415c56595b2257ad218936dd9fa726b36341b373b31ca958590fe", size = 30874 }, - { url = "https://files.pythonhosted.org/packages/d4/84/62a359b3aa51228bd88f78b44ebb0256a5b96dd2487881c1e984a59b617d/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f1f13a25fc46731acab518602bb1149bfd8b5fabedf8290a7c0926d61414769d", size = 29893 }, - { url = "https://files.pythonhosted.org/packages/e2/d6/b3c52c03ee41e7f006e1a737e0db1c58d1dc28e258b83548e653d0c34f1c/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1534d6cd3854d035e40bf4c091984cbdd4d555d7579676d406c53c8f187c006f", size = 32293 }, - { url = "https://files.pythonhosted.org/packages/55/09/c0ba311879d9c05860503a7e2708ace85913b9a816786402a92c664fe930/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62a01c76708daac78b9688ffb95268c57cb57fa90b543043cda01358912fe2db", size = 30247 }, - { url = "https://files.pythonhosted.org/packages/9e/43/cc7155461f0b5a48aebdb87d78239ff3a51ebda0905de478d9fa6ab92d9c/setproctitle-1.3.5-cp311-cp311-win32.whl", hash = "sha256:ea07f29735d839eaed985990a0ec42c8aecefe8050da89fec35533d146a7826d", size = 11476 }, - { url = "https://files.pythonhosted.org/packages/e7/57/6e937ac7aa52db69225f02db2cfdcb66ba1db6fdc65a4ddbdf78e214f72a/setproctitle-1.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab3ae11e10d13d514d4a5a15b4f619341142ba3e18da48c40e8614c5a1b5e3c3", size = 12189 }, - { url = "https://files.pythonhosted.org/packages/2b/19/04755958495de57e4891de50f03e77b3fe9ca6716a86de00faa00ad0ee5a/setproctitle-1.3.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:523424b9be4dea97d95b8a584b183f35c7bab2d0a3d995b01febf5b8a8de90e4", size = 17250 }, - { url = "https://files.pythonhosted.org/packages/b9/3d/2ca9df5aa49b975296411dcbbe272cdb1c5e514c43b8be7d61751bb71a46/setproctitle-1.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6ec1d86c1b4d7b5f2bdceadf213310cf24696b82480a2a702194b8a0bfbcb47", size = 11878 }, - { url = "https://files.pythonhosted.org/packages/36/d6/e90e23b4627e016a4f862d4f892be92c9765dd6bf1e27a48e52cd166d4a3/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea6c505264275a43e9b2acd2acfc11ac33caf52bc3167c9fced4418a810f6b1c", size = 31940 }, - { url = "https://files.pythonhosted.org/packages/15/13/167cdd55e00a8e10b36aad79646c3bf3c23fba0c08a9b8db9b74622c1b13/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b91e68e6685998e6353f296100ecabc313a6cb3e413d66a03d74b988b61f5ff", size = 33370 }, - { url = "https://files.pythonhosted.org/packages/9b/22/574a110527df133409a75053b7d6ff740993ccf30b8713d042f26840d351/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1fda208ae3a2285ad27aeab44c41daf2328abe58fa3270157a739866779199", size = 30628 }, - { url = "https://files.pythonhosted.org/packages/52/79/78b05c7d792c9167b917acdab1773b1ff73b016560f45d8155be2baa1a82/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:828727d220e46f048b82289018300a64547b46aaed96bf8810c05fe105426b41", size = 31672 }, - { url = "https://files.pythonhosted.org/packages/b0/62/4509735be062129694751ac55d5e1fbb6d86fa46a8689b7d5e2c23dae5b0/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:83b016221cf80028b2947be20630faa14e3e72a403e35f0ba29550b4e856767b", size = 31378 }, - { url = "https://files.pythonhosted.org/packages/72/e7/b394c55934b89f00c2ef7d5e6f18cca5d8dfa26ef628700c4de0c85e3f3d/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6d8a411e752e794d052434139ca4234ffeceeb8d8d8ddc390a9051d7942b2726", size = 30370 }, - { url = "https://files.pythonhosted.org/packages/13/ee/e1f27bf52d2bec7060bb6311ab0ccede8de98ed5394e3a59e7a14a453fb5/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50cfbf86b9c63a2c2903f1231f0a58edeb775e651ae1af84eec8430b0571f29b", size = 32875 }, - { url = "https://files.pythonhosted.org/packages/6e/08/13b561085d2de53b9becfa5578545d99114e9ff2aa3dc151bcaadf80b17e/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3b5e2eacd572444770026c9dd3ddc7543ce427cdf452d40a408d1e95beefb30", size = 30903 }, - { url = "https://files.pythonhosted.org/packages/65/f0/6cd06fffff2553be7b0571447d0c0ef8b727ef44cc2d6a33452677a311c8/setproctitle-1.3.5-cp312-cp312-win32.whl", hash = "sha256:cf4e3ded98027de2596c6cc5bbd3302adfb3ca315c848f56516bb0b7e88de1e9", size = 11468 }, - { url = "https://files.pythonhosted.org/packages/c1/8c/e8a7cb568c4552618838941b332203bfc77ab0f2d67c1cb8f24dee0370ec/setproctitle-1.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:f7a8c01ffd013dda2bed6e7d5cb59fbb609e72f805abf3ee98360f38f7758d9b", size = 12190 }, + { url = "https://files.pythonhosted.org/packages/ec/4a/9e0243c5df221102fb834a947f5753d9da06ad5f84e36b0e2e93f7865edb/setproctitle-1.3.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8dcc250872385f2780a5ea58050b58cbc8b6a7e8444952a5a65c359886c593", size = 17256, upload_time = "2025-02-22T21:50:45.928Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a1/76ad2ba6f5bd00609238e3d64eeded4598e742a5f25b5cc1a0efdae5f674/setproctitle-1.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ca82fae9eb4800231dd20229f06e8919787135a5581da245b8b05e864f34cc8b", size = 11893, upload_time = "2025-02-22T21:50:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/75d11fedff5b21ba9a4c5fe3dfa5e596f831d094ef1896713a72e9e38833/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0424e1d33232322541cb36fb279ea5242203cd6f20de7b4fb2a11973d8e8c2ce", size = 31631, upload_time = "2025-02-22T21:50:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/5a/12/58220de5600e0ed2e5562297173187d863db49babb03491ffe9c101299bc/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fec8340ab543144d04a9d805d80a0aad73fdeb54bea6ff94e70d39a676ea4ec0", size = 32975, upload_time = "2025-02-22T21:50:52.188Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c4/fbb308680d83c1c7aa626950308318c6e6381a8273779163a31741f3c752/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eab441c89f181271ab749077dcc94045a423e51f2fb0b120a1463ef9820a08d0", size = 30126, upload_time = "2025-02-22T21:50:53.496Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/baaf70bd9a881dd8c12cbccdd7ca0ff291024a37044a8245e942e12e7135/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c371550a2288901a0dcd84192691ebd3197a43c95f3e0b396ed6d1cedf5c6c", size = 31135, upload_time = "2025-02-22T21:50:54.931Z" }, + { url = "https://files.pythonhosted.org/packages/a6/dc/d8ab6b1c3d844dc14f596e3cce76604570848f8a67ba6a3812775ed2c015/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78288ff5f9c415c56595b2257ad218936dd9fa726b36341b373b31ca958590fe", size = 30874, upload_time = "2025-02-22T21:50:57.042Z" }, + { url = "https://files.pythonhosted.org/packages/d4/84/62a359b3aa51228bd88f78b44ebb0256a5b96dd2487881c1e984a59b617d/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f1f13a25fc46731acab518602bb1149bfd8b5fabedf8290a7c0926d61414769d", size = 29893, upload_time = "2025-02-22T21:50:59.644Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d6/b3c52c03ee41e7f006e1a737e0db1c58d1dc28e258b83548e653d0c34f1c/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1534d6cd3854d035e40bf4c091984cbdd4d555d7579676d406c53c8f187c006f", size = 32293, upload_time = "2025-02-22T21:51:01.777Z" }, + { url = "https://files.pythonhosted.org/packages/55/09/c0ba311879d9c05860503a7e2708ace85913b9a816786402a92c664fe930/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62a01c76708daac78b9688ffb95268c57cb57fa90b543043cda01358912fe2db", size = 30247, upload_time = "2025-02-22T21:51:03.779Z" }, + { url = "https://files.pythonhosted.org/packages/9e/43/cc7155461f0b5a48aebdb87d78239ff3a51ebda0905de478d9fa6ab92d9c/setproctitle-1.3.5-cp311-cp311-win32.whl", hash = "sha256:ea07f29735d839eaed985990a0ec42c8aecefe8050da89fec35533d146a7826d", size = 11476, upload_time = "2025-02-22T21:51:05.746Z" }, + { url = "https://files.pythonhosted.org/packages/e7/57/6e937ac7aa52db69225f02db2cfdcb66ba1db6fdc65a4ddbdf78e214f72a/setproctitle-1.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab3ae11e10d13d514d4a5a15b4f619341142ba3e18da48c40e8614c5a1b5e3c3", size = 12189, upload_time = "2025-02-22T21:51:07.837Z" }, + { url = "https://files.pythonhosted.org/packages/2b/19/04755958495de57e4891de50f03e77b3fe9ca6716a86de00faa00ad0ee5a/setproctitle-1.3.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:523424b9be4dea97d95b8a584b183f35c7bab2d0a3d995b01febf5b8a8de90e4", size = 17250, upload_time = "2025-02-22T21:51:09.785Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3d/2ca9df5aa49b975296411dcbbe272cdb1c5e514c43b8be7d61751bb71a46/setproctitle-1.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6ec1d86c1b4d7b5f2bdceadf213310cf24696b82480a2a702194b8a0bfbcb47", size = 11878, upload_time = "2025-02-22T21:51:11.679Z" }, + { url = "https://files.pythonhosted.org/packages/36/d6/e90e23b4627e016a4f862d4f892be92c9765dd6bf1e27a48e52cd166d4a3/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea6c505264275a43e9b2acd2acfc11ac33caf52bc3167c9fced4418a810f6b1c", size = 31940, upload_time = "2025-02-22T21:51:12.977Z" }, + { url = "https://files.pythonhosted.org/packages/15/13/167cdd55e00a8e10b36aad79646c3bf3c23fba0c08a9b8db9b74622c1b13/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b91e68e6685998e6353f296100ecabc313a6cb3e413d66a03d74b988b61f5ff", size = 33370, upload_time = "2025-02-22T21:51:15.115Z" }, + { url = "https://files.pythonhosted.org/packages/9b/22/574a110527df133409a75053b7d6ff740993ccf30b8713d042f26840d351/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1fda208ae3a2285ad27aeab44c41daf2328abe58fa3270157a739866779199", size = 30628, upload_time = "2025-02-22T21:51:16.324Z" }, + { url = "https://files.pythonhosted.org/packages/52/79/78b05c7d792c9167b917acdab1773b1ff73b016560f45d8155be2baa1a82/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:828727d220e46f048b82289018300a64547b46aaed96bf8810c05fe105426b41", size = 31672, upload_time = "2025-02-22T21:51:17.791Z" }, + { url = "https://files.pythonhosted.org/packages/b0/62/4509735be062129694751ac55d5e1fbb6d86fa46a8689b7d5e2c23dae5b0/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:83b016221cf80028b2947be20630faa14e3e72a403e35f0ba29550b4e856767b", size = 31378, upload_time = "2025-02-22T21:51:19.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/e7/b394c55934b89f00c2ef7d5e6f18cca5d8dfa26ef628700c4de0c85e3f3d/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6d8a411e752e794d052434139ca4234ffeceeb8d8d8ddc390a9051d7942b2726", size = 30370, upload_time = "2025-02-22T21:51:21.218Z" }, + { url = "https://files.pythonhosted.org/packages/13/ee/e1f27bf52d2bec7060bb6311ab0ccede8de98ed5394e3a59e7a14a453fb5/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50cfbf86b9c63a2c2903f1231f0a58edeb775e651ae1af84eec8430b0571f29b", size = 32875, upload_time = "2025-02-22T21:51:22.505Z" }, + { url = "https://files.pythonhosted.org/packages/6e/08/13b561085d2de53b9becfa5578545d99114e9ff2aa3dc151bcaadf80b17e/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3b5e2eacd572444770026c9dd3ddc7543ce427cdf452d40a408d1e95beefb30", size = 30903, upload_time = "2025-02-22T21:51:23.732Z" }, + { url = "https://files.pythonhosted.org/packages/65/f0/6cd06fffff2553be7b0571447d0c0ef8b727ef44cc2d6a33452677a311c8/setproctitle-1.3.5-cp312-cp312-win32.whl", hash = "sha256:cf4e3ded98027de2596c6cc5bbd3302adfb3ca315c848f56516bb0b7e88de1e9", size = 11468, upload_time = "2025-02-22T21:51:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8c/e8a7cb568c4552618838941b332203bfc77ab0f2d67c1cb8f24dee0370ec/setproctitle-1.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:f7a8c01ffd013dda2bed6e7d5cb59fbb609e72f805abf3ee98360f38f7758d9b", size = 12190, upload_time = "2025-02-22T21:51:26.78Z" }, ] [[package]] name = "setuptools" -version = "78.1.0" +version = "79.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } +sdist = { url = "https://files.pythonhosted.org/packages/bb/71/b6365e6325b3290e14957b2c3a804a529968c77a049b2ed40c095f749707/setuptools-79.0.1.tar.gz", hash = "sha256:128ce7b8f33c3079fd1b067ecbb4051a66e8526e7b65f6cec075dfc650ddfa88", size = 1367909, upload_time = "2025-04-23T22:20:59.241Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, + { url = "https://files.pythonhosted.org/packages/0d/6d/b4752b044bf94cb802d88a888dc7d288baaf77d7910b7dedda74b5ceea0c/setuptools-79.0.1-py3-none-any.whl", hash = "sha256:e147c0549f27767ba362f9da434eab9c5dc0045d5304feb602a0af001089fc51", size = 1256281, upload_time = "2025-04-23T22:20:56.768Z" }, ] [[package]] @@ -4817,71 +4824,71 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617 } +sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload_time = "2025-04-03T09:15:05.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/37/ae448f06f363ff3dfe4bae890abd842c4e3e9edaf01245dbc9b97008c9e6/shapely-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8323031ef7c1bdda7a92d5ddbc7b6b62702e73ba37e9a8ccc8da99ec2c0b87c", size = 1820974 }, - { url = "https://files.pythonhosted.org/packages/78/da/ea2a898e93c6953c5eef353a0e1781a0013a1352f2b90aa9ab0b800e0c75/shapely-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4da7c6cd748d86ec6aace99ad17129d30954ccf5e73e9911cdb5f0fa9658b4f8", size = 1624137 }, - { url = "https://files.pythonhosted.org/packages/64/4a/f903f82f0fabcd3f43ea2e8132cabda079119247330a9fe58018c39c4e22/shapely-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f0cdf85ff80831137067e7a237085a3ee72c225dba1b30beef87f7d396cf02b", size = 2957161 }, - { url = "https://files.pythonhosted.org/packages/92/07/3e2738c542d73182066196b8ce99388cb537d19e300e428d50b1537e3b21/shapely-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f2be5d79aac39886f23000727cf02001aef3af8810176c29ee12cdc3ef3a50", size = 3078530 }, - { url = "https://files.pythonhosted.org/packages/82/08/32210e63d8f8af9142d37c2433ece4846862cdac91a0fe66f040780a71bd/shapely-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:21a4515009f56d7a159cf5c2554264e82f56405b4721f9a422cb397237c5dca8", size = 3902208 }, - { url = "https://files.pythonhosted.org/packages/19/0e/0abb5225f8a32fbdb615476637038a7d2db40c0af46d1bb3a08b869bee39/shapely-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cebc323cec2cb6b2eaa310fdfc621f6dbbfaf6bde336d13838fcea76c885a9", size = 4082863 }, - { url = "https://files.pythonhosted.org/packages/f8/1b/7cd816fd388108c872ab7e2930180b02d0c34891213f361e4a66e5e032f2/shapely-2.1.0-cp311-cp311-win32.whl", hash = "sha256:cad51b7a5c8f82f5640472944a74f0f239123dde9a63042b3c5ea311739b7d20", size = 1527488 }, - { url = "https://files.pythonhosted.org/packages/fd/28/7bb5b1944d4002d4b2f967762018500381c3b532f98e456bbda40c3ded68/shapely-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4005309dde8658e287ad9c435c81877f6a95a9419b932fa7a1f34b120f270ae", size = 1708311 }, - { url = "https://files.pythonhosted.org/packages/4e/d1/6a9371ec39d3ef08e13225594e6c55b045209629afd9e6d403204507c2a8/shapely-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53e7ee8bd8609cf12ee6dce01ea5affe676976cf7049315751d53d8db6d2b4b2", size = 1830732 }, - { url = "https://files.pythonhosted.org/packages/32/87/799e3e48be7ce848c08509b94d2180f4ddb02e846e3c62d0af33da4d78d3/shapely-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cab20b665d26dbec0b380e15749bea720885a481fa7b1eedc88195d4a98cfa4", size = 1638404 }, - { url = "https://files.pythonhosted.org/packages/85/00/6665d77f9dd09478ab0993b8bc31668aec4fd3e5f1ddd1b28dd5830e47be/shapely-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a38b39a09340273c3c92b3b9a374272a12cc7e468aeeea22c1c46217a03e5c", size = 2945316 }, - { url = "https://files.pythonhosted.org/packages/34/49/738e07d10bbc67cae0dcfe5a484c6e518a517f4f90550dda2adf3a78b9f2/shapely-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edaec656bdd9b71278b98e6f77c464b1c3b2daa9eace78012ff0f0b4b5b15b04", size = 3063099 }, - { url = "https://files.pythonhosted.org/packages/88/b8/138098674559362ab29f152bff3b6630de423378fbb0324812742433a4ef/shapely-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c8a732ddd9b25e7a54aa748e7df8fd704e23e5d5d35b7d376d80bffbfc376d04", size = 3887873 }, - { url = "https://files.pythonhosted.org/packages/67/a8/fdae7c2db009244991d86f4d2ca09d2f5ccc9d41c312c3b1ee1404dc55da/shapely-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9c93693ad8adfdc9138a5a2d42da02da94f728dd2e82d2f0f442f10e25027f5f", size = 4067004 }, - { url = "https://files.pythonhosted.org/packages/ed/78/17e17d91b489019379df3ee1afc4bd39787b232aaa1d540f7d376f0280b7/shapely-2.1.0-cp312-cp312-win32.whl", hash = "sha256:d8ac6604eefe807e71a908524de23a37920133a1729fe3a4dfe0ed82c044cbf4", size = 1527366 }, - { url = "https://files.pythonhosted.org/packages/b8/bd/9249bd6dda948441e25e4fb14cbbb5205146b0fff12c66b19331f1ff2141/shapely-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:f4f47e631aa4f9ec5576eac546eb3f38802e2f82aeb0552f9612cb9a14ece1db", size = 1708265 }, + { url = "https://files.pythonhosted.org/packages/1c/37/ae448f06f363ff3dfe4bae890abd842c4e3e9edaf01245dbc9b97008c9e6/shapely-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8323031ef7c1bdda7a92d5ddbc7b6b62702e73ba37e9a8ccc8da99ec2c0b87c", size = 1820974, upload_time = "2025-04-03T09:14:11.301Z" }, + { url = "https://files.pythonhosted.org/packages/78/da/ea2a898e93c6953c5eef353a0e1781a0013a1352f2b90aa9ab0b800e0c75/shapely-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4da7c6cd748d86ec6aace99ad17129d30954ccf5e73e9911cdb5f0fa9658b4f8", size = 1624137, upload_time = "2025-04-03T09:14:13.127Z" }, + { url = "https://files.pythonhosted.org/packages/64/4a/f903f82f0fabcd3f43ea2e8132cabda079119247330a9fe58018c39c4e22/shapely-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f0cdf85ff80831137067e7a237085a3ee72c225dba1b30beef87f7d396cf02b", size = 2957161, upload_time = "2025-04-03T09:14:15.031Z" }, + { url = "https://files.pythonhosted.org/packages/92/07/3e2738c542d73182066196b8ce99388cb537d19e300e428d50b1537e3b21/shapely-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f2be5d79aac39886f23000727cf02001aef3af8810176c29ee12cdc3ef3a50", size = 3078530, upload_time = "2025-04-03T09:14:16.562Z" }, + { url = "https://files.pythonhosted.org/packages/82/08/32210e63d8f8af9142d37c2433ece4846862cdac91a0fe66f040780a71bd/shapely-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:21a4515009f56d7a159cf5c2554264e82f56405b4721f9a422cb397237c5dca8", size = 3902208, upload_time = "2025-04-03T09:14:18.342Z" }, + { url = "https://files.pythonhosted.org/packages/19/0e/0abb5225f8a32fbdb615476637038a7d2db40c0af46d1bb3a08b869bee39/shapely-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cebc323cec2cb6b2eaa310fdfc621f6dbbfaf6bde336d13838fcea76c885a9", size = 4082863, upload_time = "2025-04-03T09:14:20.233Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1b/7cd816fd388108c872ab7e2930180b02d0c34891213f361e4a66e5e032f2/shapely-2.1.0-cp311-cp311-win32.whl", hash = "sha256:cad51b7a5c8f82f5640472944a74f0f239123dde9a63042b3c5ea311739b7d20", size = 1527488, upload_time = "2025-04-03T09:14:21.597Z" }, + { url = "https://files.pythonhosted.org/packages/fd/28/7bb5b1944d4002d4b2f967762018500381c3b532f98e456bbda40c3ded68/shapely-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4005309dde8658e287ad9c435c81877f6a95a9419b932fa7a1f34b120f270ae", size = 1708311, upload_time = "2025-04-03T09:14:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d1/6a9371ec39d3ef08e13225594e6c55b045209629afd9e6d403204507c2a8/shapely-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53e7ee8bd8609cf12ee6dce01ea5affe676976cf7049315751d53d8db6d2b4b2", size = 1830732, upload_time = "2025-04-03T09:14:25.047Z" }, + { url = "https://files.pythonhosted.org/packages/32/87/799e3e48be7ce848c08509b94d2180f4ddb02e846e3c62d0af33da4d78d3/shapely-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cab20b665d26dbec0b380e15749bea720885a481fa7b1eedc88195d4a98cfa4", size = 1638404, upload_time = "2025-04-03T09:14:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/85/00/6665d77f9dd09478ab0993b8bc31668aec4fd3e5f1ddd1b28dd5830e47be/shapely-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a38b39a09340273c3c92b3b9a374272a12cc7e468aeeea22c1c46217a03e5c", size = 2945316, upload_time = "2025-04-03T09:14:28.266Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/738e07d10bbc67cae0dcfe5a484c6e518a517f4f90550dda2adf3a78b9f2/shapely-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edaec656bdd9b71278b98e6f77c464b1c3b2daa9eace78012ff0f0b4b5b15b04", size = 3063099, upload_time = "2025-04-03T09:14:30.067Z" }, + { url = "https://files.pythonhosted.org/packages/88/b8/138098674559362ab29f152bff3b6630de423378fbb0324812742433a4ef/shapely-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c8a732ddd9b25e7a54aa748e7df8fd704e23e5d5d35b7d376d80bffbfc376d04", size = 3887873, upload_time = "2025-04-03T09:14:31.912Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fdae7c2db009244991d86f4d2ca09d2f5ccc9d41c312c3b1ee1404dc55da/shapely-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9c93693ad8adfdc9138a5a2d42da02da94f728dd2e82d2f0f442f10e25027f5f", size = 4067004, upload_time = "2025-04-03T09:14:33.976Z" }, + { url = "https://files.pythonhosted.org/packages/ed/78/17e17d91b489019379df3ee1afc4bd39787b232aaa1d540f7d376f0280b7/shapely-2.1.0-cp312-cp312-win32.whl", hash = "sha256:d8ac6604eefe807e71a908524de23a37920133a1729fe3a4dfe0ed82c044cbf4", size = 1527366, upload_time = "2025-04-03T09:14:35.348Z" }, + { url = "https://files.pythonhosted.org/packages/b8/bd/9249bd6dda948441e25e4fb14cbbb5205146b0fff12c66b19331f1ff2141/shapely-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:f4f47e631aa4f9ec5576eac546eb3f38802e2f82aeb0552f9612cb9a14ece1db", size = 1708265, upload_time = "2025-04-03T09:14:36.878Z" }, ] [[package]] name = "siphash24" version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801 } +sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801, upload_time = "2024-10-15T13:41:51.924Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493 }, - { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350 }, - { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567 }, - { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630 }, - { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648 }, - { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046 }, - { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084 }, - { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233 }, - { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188 }, - { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946 }, - { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323 }, - { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000 }, + { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493, upload_time = "2024-10-15T13:41:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350, upload_time = "2024-10-15T13:41:16.262Z" }, + { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567, upload_time = "2024-10-15T13:41:17.435Z" }, + { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630, upload_time = "2024-10-15T13:41:18.578Z" }, + { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648, upload_time = "2024-10-15T13:41:19.606Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046, upload_time = "2024-10-15T13:41:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084, upload_time = "2024-10-15T13:41:21.776Z" }, + { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233, upload_time = "2024-10-15T13:41:22.787Z" }, + { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188, upload_time = "2024-10-15T13:41:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946, upload_time = "2024-10-15T13:41:25.633Z" }, + { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323, upload_time = "2024-10-15T13:41:27.349Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000, upload_time = "2024-10-15T13:41:28.364Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload_time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload_time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "smbus2" version = "0.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/c9/6d85aa809e107adf85303010a59b340be109c8f815cbedc5c08c73bcffef/smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035", size = 16950 } +sdist = { url = "https://files.pythonhosted.org/packages/10/c9/6d85aa809e107adf85303010a59b340be109c8f815cbedc5c08c73bcffef/smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035", size = 16950, upload_time = "2024-10-19T09:20:56.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/9f/2235ba9001e3c29fc342eeb222104420bcb7bac51555f0c034376a744075/smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949", size = 11527 }, + { url = "https://files.pythonhosted.org/packages/85/9f/2235ba9001e3c29fc342eeb222104420bcb7bac51555f0c034376a744075/smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949", size = 11527, upload_time = "2024-10-19T09:20:55.202Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload_time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload_time = "2021-05-16T22:03:41.177Z" }, ] [[package]] @@ -4891,19 +4898,19 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896 } +sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload_time = "2024-10-12T09:40:12.24Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276 }, - { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916 }, - { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494 }, - { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634 }, + { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload_time = "2024-10-12T09:40:05.605Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload_time = "2024-10-12T09:40:07.436Z" }, + { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload_time = "2024-10-12T09:40:09.355Z" }, + { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload_time = "2024-10-12T09:40:11.065Z" }, ] [[package]] name = "spidev" version = "3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/401c0a7be089e02826cf2c201f489876b601f15be100fe391ef9c2faed83/spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b", size = 11917 } +sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/401c0a7be089e02826cf2c201f489876b601f15be100fe391ef9c2faed83/spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b", size = 11917, upload_time = "2022-11-03T20:39:08.257Z" } [[package]] name = "sympy" @@ -4912,47 +4919,47 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196 } +sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196, upload_time = "2024-09-18T21:54:25.591Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483 }, + { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483, upload_time = "2024-09-18T21:54:23.097Z" }, ] [[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 } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload_time = "2022-10-06T17:21:48.54Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload_time = "2022-10-06T17:21:44.262Z" }, ] [[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 } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload_time = "2024-11-27T22:38:36.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, - { 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/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload_time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload_time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload_time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload_time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload_time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload_time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload_time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload_time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload_time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload_time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload_time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload_time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload_time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload_time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload_time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload_time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload_time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload_time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload_time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload_time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload_time = "2024-11-27T22:38:35.385Z" }, ] [[package]] @@ -4962,9 +4969,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload_time = "2024-11-24T20:12:22.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload_time = "2024-11-24T20:12:19.698Z" }, ] [[package]] @@ -4974,69 +4981,69 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995 } +sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload_time = "2025-03-28T02:55:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663 }, + { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload_time = "2025-03-28T02:55:11.946Z" }, ] [[package]] name = "types-tabulate" version = "0.9.0.20241207" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/43/16030404a327e4ff8c692f2273854019ed36718667b2993609dc37d14dd4/types_tabulate-0.9.0.20241207.tar.gz", hash = "sha256:ac1ac174750c0a385dfd248edc6279fa328aaf4ea317915ab879a2ec47833230", size = 8195 } +sdist = { url = "https://files.pythonhosted.org/packages/3f/43/16030404a327e4ff8c692f2273854019ed36718667b2993609dc37d14dd4/types_tabulate-0.9.0.20241207.tar.gz", hash = "sha256:ac1ac174750c0a385dfd248edc6279fa328aaf4ea317915ab879a2ec47833230", size = 8195, upload_time = "2024-12-07T02:54:42.554Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/86/a9ebfd509cbe74471106dffed320e208c72537f9aeb0a55eaa6b1b5e4d17/types_tabulate-0.9.0.20241207-py3-none-any.whl", hash = "sha256:b8dad1343c2a8ba5861c5441370c3e35908edd234ff036d4298708a1d4cf8a85", size = 8307 }, + { url = "https://files.pythonhosted.org/packages/5e/86/a9ebfd509cbe74471106dffed320e208c72537f9aeb0a55eaa6b1b5e4d17/types_tabulate-0.9.0.20241207-py3-none-any.whl", hash = "sha256:b8dad1343c2a8ba5861c5441370c3e35908edd234ff036d4298708a1d4cf8a85", size = 8307, upload_time = "2024-12-07T02:54:41.031Z" }, ] [[package]] name = "typing-extensions" version = "4.13.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload_time = "2025-04-10T14:19:05.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload_time = "2025-04-10T14:19:03.967Z" }, ] [[package]] name = "urllib3" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload_time = "2025-04-10T15:23:39.232Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload_time = "2025-04-10T15:23:37.377Z" }, ] [[package]] name = "watchdog" 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 } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload_time = "2024-11-01T14:07:13.037Z" } wheels = [ - { 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/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 }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, + { 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, upload_time = "2024-11-01T14:06:31.756Z" }, + { 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, upload_time = "2024-11-01T14:06:32.99Z" }, + { 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, upload_time = "2024-11-01T14:06:34.963Z" }, + { 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, upload_time = "2024-11-01T14:06:37.745Z" }, + { 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, upload_time = "2024-11-01T14:06:39.748Z" }, + { 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, upload_time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload_time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload_time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload_time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload_time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload_time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload_time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload_time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload_time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload_time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload_time = "2024-11-01T14:07:11.845Z" }, ] [[package]] name = "websocket-client" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648 } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload_time = "2024-04-23T22:16:16.976Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 }, + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload_time = "2024-04-23T22:16:14.422Z" }, ] [[package]] @@ -5046,57 +5053,57 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907 } +sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload_time = "2024-11-14T00:11:41.584Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158 }, + { url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158, upload_time = "2024-11-14T00:11:39.37Z" }, ] [[package]] name = "yarl" -version = "1.19.0" +version = "1.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/4d/8a8f57caccce49573e567744926f88c6ab3ca0b47a257806d1cf88584c5f/yarl-1.19.0.tar.gz", hash = "sha256:01e02bb80ae0dbed44273c304095295106e1d9470460e773268a27d11e594892", size = 184396 } +sdist = { url = "https://files.pythonhosted.org/packages/62/51/c0edba5219027f6eab262e139f73e2417b0f4efffa23bf562f6e18f76ca5/yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307", size = 185258, upload_time = "2025-04-17T00:45:14.661Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/df/5fa7cd75e46306e0f9baf38a7c8969ff6730ea503b86232e85cb740304cf/yarl-1.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:163ff326680de5f6d4966954cf9e3fe1bf980f5fee2255e46e89b8cf0f3418b5", size = 145126 }, - { url = "https://files.pythonhosted.org/packages/2a/be/c1b52129cd2166ab7337f08e701a61baa7c260c7b03b534098cc8297aecc/yarl-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a626c4d9cca298d1be8625cff4b17004a9066330ac82d132bbda64a4c17c18d3", size = 96691 }, - { url = "https://files.pythonhosted.org/packages/8d/39/ad62139b45515f9bf129c805aeaaedf86fd93ae57ffe911f4caeabef3e74/yarl-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:961c3e401ea7f13d02b8bb7cb0c709152a632a6e14cdc8119e9c6ee5596cd45d", size = 94505 }, - { url = "https://files.pythonhosted.org/packages/be/be/04e3202cdc9bb5f81761e327af7095cffb0d81e32421a6b87f926052d2ae/yarl-1.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a39d7b807ab58e633ed760f80195cbd145b58ba265436af35f9080f1810dfe64", size = 355485 }, - { url = "https://files.pythonhosted.org/packages/00/7d/1463203663ca1ae62af8fb9ebc9601dd07f04dbced7edb1df3141a2cb2fe/yarl-1.19.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4228978fb59c6b10f60124ba8e311c26151e176df364e996f3f8ff8b93971b5", size = 344569 }, - { url = "https://files.pythonhosted.org/packages/b0/1b/5263203017348669e637bb73856fb9632110538e92d5e9f8214fcc764da9/yarl-1.19.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ba536b17ecf3c74a94239ec1137a3ad3caea8c0e4deb8c8d2ffe847d870a8c5", size = 371426 }, - { url = "https://files.pythonhosted.org/packages/78/59/90ca5f16d56b7741e5383951acc2e065fce41920eb5d8fda3065b5e288dc/yarl-1.19.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a251e00e445d2e9df7b827c9843c0b87f58a3254aaa3f162fb610747491fe00f", size = 368102 }, - { url = "https://files.pythonhosted.org/packages/84/f2/5e33aa0251ffd2c2a9041bf887e163eeefdc1dca238fdabac444d9463c3f/yarl-1.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9b92431d8b4d4ca5ccbfdbac95b05a3a6cd70cd73aa62f32f9627acfde7549c", size = 358740 }, - { url = "https://files.pythonhosted.org/packages/22/9e/ba92d234c81cf94495fc01eaa0b6000175733f76bd63e60ff748bce22c81/yarl-1.19.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec2f56edaf476f70b5831bbd59700b53d9dd011b1f77cd4846b5ab5c5eafdb3f", size = 346965 }, - { url = "https://files.pythonhosted.org/packages/8d/0b/d4f53136ef12ddad540855a886d7503a6cc17cfabb9a03ce0c179f3b9e51/yarl-1.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acf9b92c4245ac8b59bc7ec66a38d3dcb8d1f97fac934672529562bb824ecadb", size = 368547 }, - { url = "https://files.pythonhosted.org/packages/31/4b/35ec8622908a728f378a8511f0ab2d47878b2c0b8cbe035f2d907914a5fc/yarl-1.19.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:57711f1465c06fee8825b95c0b83e82991e6d9425f9a042c3c19070a70ac92bf", size = 357610 }, - { url = "https://files.pythonhosted.org/packages/c1/71/1f39f7c55b0684834d945a2bcfdfe59e6e02ca2483a3d33c2f77a0c3b177/yarl-1.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:528e86f5b1de0ad8dd758ddef4e0ed24f5d946d4a1cef80ffb2d4fca4e10f122", size = 365331 }, - { url = "https://files.pythonhosted.org/packages/2e/13/57675964de5c8ccf6427df93ac97f9bb7328f3f8f7ebc31a5f5a286ab1c0/yarl-1.19.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3b77173663e075d9e5a57e09d711e9da2f3266be729ecca0b8ae78190990d260", size = 378624 }, - { url = "https://files.pythonhosted.org/packages/d4/c6/5868e40f8da041ed0c3b5fd8c08cece849d9f609e970e6043308767fbb60/yarl-1.19.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d8717924cf0a825b62b1a96fc7d28aab7f55a81bf5338b8ef41d7a76ab9223e9", size = 383981 }, - { url = "https://files.pythonhosted.org/packages/f4/3f/e40124c986d96741d3d341ffac35be42b6df82ef8c18b5984ca2e7d838dd/yarl-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0df9f0221a78d858793f40cbea3915c29f969c11366646a92ca47e080a14f881", size = 378868 }, - { url = "https://files.pythonhosted.org/packages/01/eb/caf2774c770288bd87a818b11f3a56ada6a855f1987d93421aae01a175bf/yarl-1.19.0-cp311-cp311-win32.whl", hash = "sha256:8b3ade62678ee2c7c10dcd6be19045135e9badad53108f7d2ed14896ee396045", size = 86446 }, - { url = "https://files.pythonhosted.org/packages/4a/97/d4fe6168c1bb789507ffeb58c2e8c675a7e71de732dc02e12bda904c1362/yarl-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:0626ee31edb23ac36bdffe607231de2cca055ad3a5e2dc5da587ef8bc6a321bc", size = 93121 }, - { url = "https://files.pythonhosted.org/packages/b8/70/44ef8f69d61cb5123167a4dda87f6c739a833fbdb2ed52960b4e8409d65c/yarl-1.19.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b687c334da3ff8eab848c9620c47a253d005e78335e9ce0d6868ed7e8fd170b", size = 146855 }, - { url = "https://files.pythonhosted.org/packages/c3/94/38c14d6c8217cc818647689f2dd647b976ced8fea08d0ac84e3c8168252b/yarl-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b0fe766febcf523a2930b819c87bb92407ae1368662c1bc267234e79b20ff894", size = 97523 }, - { url = "https://files.pythonhosted.org/packages/35/a5/43a613586a6255105c4655a911c307ef3420e49e540d6ae2c5829863fb25/yarl-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:742ceffd3c7beeb2b20d47cdb92c513eef83c9ef88c46829f88d5b06be6734ee", size = 95540 }, - { url = "https://files.pythonhosted.org/packages/d4/60/ed26049f4a8b06ebfa6d5f3cb6a51b152fd57081aa818b6497474f65a631/yarl-1.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2af682a1e97437382ee0791eacbf540318bd487a942e068e7e0a6c571fadbbd3", size = 344386 }, - { url = "https://files.pythonhosted.org/packages/49/a6/b84899cab411f49af5986cfb44b514040788d81c8084f5811e6a7c0f1ce6/yarl-1.19.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:63702f1a098d0eaaea755e9c9d63172be1acb9e2d4aeb28b187092bcc9ca2d17", size = 338889 }, - { url = "https://files.pythonhosted.org/packages/cc/ce/0704f7166a781b1f81bdd45c4f49eadbae0230ebd35b9ec7cd7769d3a6ff/yarl-1.19.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3560dcba3c71ae7382975dc1e912ee76e50b4cd7c34b454ed620d55464f11876", size = 353107 }, - { url = "https://files.pythonhosted.org/packages/75/e5/0ecd6f2a9cc4264c16d8dfb0d3d71ba8d03cb58f3bcd42b1df4358331189/yarl-1.19.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68972df6a0cc47c8abaf77525a76ee5c5f6ea9bbdb79b9565b3234ded3c5e675", size = 353128 }, - { url = "https://files.pythonhosted.org/packages/ad/c7/cd0fd1de581f1c2e8f996e704c9fd979e00106f18eebd91b0173cf1a13c6/yarl-1.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5684e7ff93ea74e47542232bd132f608df4d449f8968fde6b05aaf9e08a140f9", size = 349107 }, - { url = "https://files.pythonhosted.org/packages/e6/34/ba3e5a20bd1d6a09034fc7985aaf1309976f2a7a5aefd093c9e56f6e1e0c/yarl-1.19.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8182ad422bfacdebd4759ce3adc6055c0c79d4740aea1104e05652a81cd868c6", size = 335144 }, - { url = "https://files.pythonhosted.org/packages/1e/98/d9b7beb932fade015906efe0980aa7d522b8f93cf5ebf1082e74faa314b7/yarl-1.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aee5b90a5a9b71ac57400a7bdd0feaa27c51e8f961decc8d412e720a004a1791", size = 360795 }, - { url = "https://files.pythonhosted.org/packages/9a/11/70b8770039cc54af5948970591517a1e1d093df3f04f328c655c9a0fefb7/yarl-1.19.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8c0b2371858d5a814b08542d5d548adb03ff2d7ab32f23160e54e92250961a72", size = 360140 }, - { url = "https://files.pythonhosted.org/packages/d4/67/708e3e36fafc4d9d96b4eecc6c8b9f37c8ad50df8a16c7a1d5ba9df53050/yarl-1.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cd430c2b7df4ae92498da09e9b12cad5bdbb140d22d138f9e507de1aa3edfea3", size = 364431 }, - { url = "https://files.pythonhosted.org/packages/c3/8b/937fbbcc895553a7e16fcd86ae4e0724c6ac9468237ad8e7c29cc3b1c9d9/yarl-1.19.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a93208282c0ccdf73065fd76c6c129bd428dba5ff65d338ae7d2ab27169861a0", size = 373832 }, - { url = "https://files.pythonhosted.org/packages/f8/ca/288ddc2230c9b6647fe907504f1119adb41252ac533eb564d3fc73511215/yarl-1.19.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b8179280cdeb4c36eb18d6534a328f9d40da60d2b96ac4a295c5f93e2799e9d9", size = 378122 }, - { url = "https://files.pythonhosted.org/packages/4f/5a/79e1ef31d14968fbfc0ecec70a6683b574890d9c7550c376dd6d40de7754/yarl-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eda3c2b42dc0c389b7cfda2c4df81c12eeb552019e0de28bde8f913fc3d1fcf3", size = 375178 }, - { url = "https://files.pythonhosted.org/packages/95/38/9b0e56bf14026c3f550ad6425679f6d1a2f4821d70767f39d6f4c56a0820/yarl-1.19.0-cp312-cp312-win32.whl", hash = "sha256:57f3fed859af367b9ca316ecc05ce79ce327d6466342734305aa5cc380e4d8be", size = 86172 }, - { url = "https://files.pythonhosted.org/packages/b3/96/5c2f3987c4bb4e5cdebea3caf99a45946b13a9516f849c02222203d99860/yarl-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:5507c1f7dd3d41251b67eecba331c8b2157cfd324849879bebf74676ce76aff7", size = 92617 }, - { url = "https://files.pythonhosted.org/packages/a4/06/ae25a353e8f032322df6f30d6bb1fc329773ee48e1a80a2196ccb8d1206b/yarl-1.19.0-py3-none-any.whl", hash = "sha256:a727101eb27f66727576630d02985d8a065d09cd0b5fcbe38a5793f71b2a97ef", size = 45990 }, + { url = "https://files.pythonhosted.org/packages/60/82/a59d8e21b20ffc836775fa7daedac51d16bb8f3010c4fcb495c4496aa922/yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3", size = 145178, upload_time = "2025-04-17T00:42:04.511Z" }, + { url = "https://files.pythonhosted.org/packages/ba/81/315a3f6f95947cfbf37c92d6fbce42a1a6207b6c38e8c2b452499ec7d449/yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a", size = 96859, upload_time = "2025-04-17T00:42:06.43Z" }, + { url = "https://files.pythonhosted.org/packages/ad/17/9b64e575583158551b72272a1023cdbd65af54fe13421d856b2850a6ddb7/yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2", size = 94647, upload_time = "2025-04-17T00:42:07.976Z" }, + { url = "https://files.pythonhosted.org/packages/2c/29/8f291e7922a58a21349683f6120a85701aeefaa02e9f7c8a2dc24fe3f431/yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e", size = 355788, upload_time = "2025-04-17T00:42:09.902Z" }, + { url = "https://files.pythonhosted.org/packages/26/6d/b4892c80b805c42c228c6d11e03cafabf81662d371b0853e7f0f513837d5/yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9", size = 344613, upload_time = "2025-04-17T00:42:11.768Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0e/517aa28d3f848589bae9593717b063a544b86ba0a807d943c70f48fcf3bb/yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a", size = 370953, upload_time = "2025-04-17T00:42:13.983Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/5bd09d2f1ad6e6f7c2beae9e50db78edd2cca4d194d227b958955573e240/yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2", size = 369204, upload_time = "2025-04-17T00:42:16.386Z" }, + { url = "https://files.pythonhosted.org/packages/9c/85/d793a703cf4bd0d4cd04e4b13cc3d44149470f790230430331a0c1f52df5/yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2", size = 358108, upload_time = "2025-04-17T00:42:18.622Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/b6c71e13549c1f6048fbc14ce8d930ac5fb8bafe4f1a252e621a24f3f1f9/yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8", size = 346610, upload_time = "2025-04-17T00:42:20.9Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1a/d6087d58bdd0d8a2a37bbcdffac9d9721af6ebe50d85304d9f9b57dfd862/yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902", size = 365378, upload_time = "2025-04-17T00:42:22.926Z" }, + { url = "https://files.pythonhosted.org/packages/02/84/e25ddff4cbc001dbc4af76f8d41a3e23818212dd1f0a52044cbc60568872/yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791", size = 356919, upload_time = "2025-04-17T00:42:25.145Z" }, + { url = "https://files.pythonhosted.org/packages/04/76/898ae362353bf8f64636495d222c8014c8e5267df39b1a9fe1e1572fb7d0/yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f", size = 364248, upload_time = "2025-04-17T00:42:27.475Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b0/9d9198d83a622f1c40fdbf7bd13b224a6979f2e1fc2cf50bfb1d8773c495/yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da", size = 378418, upload_time = "2025-04-17T00:42:29.333Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ce/1f50c1cc594cf5d3f5bf4a9b616fca68680deaec8ad349d928445ac52eb8/yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4", size = 383850, upload_time = "2025-04-17T00:42:31.668Z" }, + { url = "https://files.pythonhosted.org/packages/89/1e/a59253a87b35bfec1a25bb5801fb69943330b67cfd266278eb07e0609012/yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5", size = 381218, upload_time = "2025-04-17T00:42:33.523Z" }, + { url = "https://files.pythonhosted.org/packages/85/b0/26f87df2b3044b0ef1a7cf66d321102bdca091db64c5ae853fcb2171c031/yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6", size = 86606, upload_time = "2025-04-17T00:42:35.873Z" }, + { url = "https://files.pythonhosted.org/packages/33/46/ca335c2e1f90446a77640a45eeb1cd8f6934f2c6e4df7db0f0f36ef9f025/yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb", size = 93374, upload_time = "2025-04-17T00:42:37.586Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e8/3efdcb83073df978bb5b1a9cc0360ce596680e6c3fac01f2a994ccbb8939/yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f", size = 147089, upload_time = "2025-04-17T00:42:39.602Z" }, + { url = "https://files.pythonhosted.org/packages/60/c3/9e776e98ea350f76f94dd80b408eaa54e5092643dbf65fd9babcffb60509/yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e", size = 97706, upload_time = "2025-04-17T00:42:41.469Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/45cdfb64a3b855ce074ae607b9fc40bc82e7613b94e7612b030255c93a09/yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e", size = 95719, upload_time = "2025-04-17T00:42:43.666Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4e/929633b249611eeed04e2f861a14ed001acca3ef9ec2a984a757b1515889/yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33", size = 343972, upload_time = "2025-04-17T00:42:45.391Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/047535d326c913f1a90407a3baf7ff535b10098611eaef2c527e32e81ca1/yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58", size = 339639, upload_time = "2025-04-17T00:42:47.552Z" }, + { url = "https://files.pythonhosted.org/packages/48/2f/11566f1176a78f4bafb0937c0072410b1b0d3640b297944a6a7a556e1d0b/yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f", size = 353745, upload_time = "2025-04-17T00:42:49.406Z" }, + { url = "https://files.pythonhosted.org/packages/26/17/07dfcf034d6ae8837b33988be66045dd52f878dfb1c4e8f80a7343f677be/yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae", size = 354178, upload_time = "2025-04-17T00:42:51.588Z" }, + { url = "https://files.pythonhosted.org/packages/15/45/212604d3142d84b4065d5f8cab6582ed3d78e4cc250568ef2a36fe1cf0a5/yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018", size = 349219, upload_time = "2025-04-17T00:42:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e0/a10b30f294111c5f1c682461e9459935c17d467a760c21e1f7db400ff499/yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672", size = 337266, upload_time = "2025-04-17T00:42:55.49Z" }, + { url = "https://files.pythonhosted.org/packages/33/a6/6efa1d85a675d25a46a167f9f3e80104cde317dfdf7f53f112ae6b16a60a/yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8", size = 360873, upload_time = "2025-04-17T00:42:57.895Z" }, + { url = "https://files.pythonhosted.org/packages/77/67/c8ab718cb98dfa2ae9ba0f97bf3cbb7d45d37f13fe1fbad25ac92940954e/yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7", size = 360524, upload_time = "2025-04-17T00:43:00.094Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/c3f18660cea1bc73d9f8a2b3ef423def8dadbbae6c4afabdb920b73e0ead/yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594", size = 365370, upload_time = "2025-04-17T00:43:02.242Z" }, + { url = "https://files.pythonhosted.org/packages/c9/99/33f3b97b065e62ff2d52817155a89cfa030a1a9b43fee7843ef560ad9603/yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6", size = 373297, upload_time = "2025-04-17T00:43:04.189Z" }, + { url = "https://files.pythonhosted.org/packages/3d/89/7519e79e264a5f08653d2446b26d4724b01198a93a74d2e259291d538ab1/yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1", size = 378771, upload_time = "2025-04-17T00:43:06.609Z" }, + { url = "https://files.pythonhosted.org/packages/3a/58/6c460bbb884abd2917c3eef6f663a4a873f8dc6f498561fc0ad92231c113/yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b", size = 375000, upload_time = "2025-04-17T00:43:09.01Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/dd7ed1aa23fea996834278d7ff178f215b24324ee527df53d45e34d21d28/yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64", size = 86355, upload_time = "2025-04-17T00:43:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c6/333fe0338305c0ac1c16d5aa7cc4841208d3252bbe62172e0051006b5445/yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c", size = 92904, upload_time = "2025-04-17T00:43:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1f/70c57b3d7278e94ed22d85e09685d3f0a38ebdd8c5c73b65ba4c0d0fe002/yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124", size = 46124, upload_time = "2025-04-17T00:45:12.199Z" }, ] [[package]] @@ -5106,38 +5113,38 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 } +sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload_time = "2024-07-15T00:18:06.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699 }, - { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681 }, - { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328 }, - { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955 }, - { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944 }, - { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927 }, - { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910 }, - { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544 }, - { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094 }, - { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440 }, - { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091 }, - { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682 }, - { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707 }, - { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792 }, - { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586 }, - { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420 }, - { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713 }, - { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459 }, - { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707 }, - { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545 }, - { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533 }, - { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510 }, - { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973 }, - { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968 }, - { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179 }, - { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577 }, - { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899 }, - { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964 }, - { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398 }, - { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313 }, - { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877 }, - { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595 }, + { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload_time = "2024-07-15T00:14:04.909Z" }, + { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload_time = "2024-07-15T00:14:13.99Z" }, + { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload_time = "2024-07-15T00:14:16.588Z" }, + { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload_time = "2024-07-15T00:14:19.389Z" }, + { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload_time = "2024-07-15T00:14:22.173Z" }, + { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload_time = "2024-07-15T00:14:24.825Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload_time = "2024-07-15T00:14:26.982Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload_time = "2024-07-15T00:14:29.582Z" }, + { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload_time = "2024-07-15T00:14:40.126Z" }, + { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload_time = "2024-07-15T00:14:42.786Z" }, + { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload_time = "2024-07-15T00:14:45.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload_time = "2024-07-15T00:14:47.407Z" }, + { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload_time = "2024-07-15T00:15:03.529Z" }, + { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload_time = "2024-07-15T00:15:28.372Z" }, + { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload_time = "2024-07-15T00:15:32.26Z" }, + { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload_time = "2024-07-15T00:15:34.004Z" }, + { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload_time = "2024-07-15T00:15:35.815Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload_time = "2024-07-15T00:15:37.995Z" }, + { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload_time = "2024-07-15T00:15:39.872Z" }, + { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload_time = "2024-07-15T00:15:41.75Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload_time = "2024-07-15T00:15:44.114Z" }, + { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload_time = "2024-07-15T00:15:46.509Z" }, + { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload_time = "2024-07-15T00:15:49.939Z" }, + { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload_time = "2024-07-15T00:15:52.025Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload_time = "2024-07-15T00:15:54.971Z" }, + { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload_time = "2024-07-15T00:15:57.634Z" }, + { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload_time = "2024-07-15T00:16:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload_time = "2024-07-15T00:16:03.669Z" }, + { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload_time = "2024-07-15T00:16:06.694Z" }, + { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload_time = "2024-07-15T00:16:09.758Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload_time = "2024-07-15T00:16:11.758Z" }, + { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload_time = "2024-07-15T00:16:13.731Z" }, ] From 41b34c6f43a2f3777d570300551595fc75d81591 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 14:00:28 -0700 Subject: [PATCH 074/477] [bot] Update translations (#35072) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 8 -------- selfdrive/ui/translations/main_de.ts | 8 -------- selfdrive/ui/translations/main_es.ts | 8 -------- selfdrive/ui/translations/main_fr.ts | 8 -------- selfdrive/ui/translations/main_ja.ts | 8 -------- selfdrive/ui/translations/main_ko.ts | 8 -------- selfdrive/ui/translations/main_pt-BR.ts | 8 -------- selfdrive/ui/translations/main_th.ts | 8 -------- selfdrive/ui/translations/main_tr.ts | 8 -------- selfdrive/ui/translations/main_zh-CHS.ts | 8 -------- selfdrive/ui/translations/main_zh-CHT.ts | 8 -------- 11 files changed, 88 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 2afc088b24..fa3b2d69e7 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -582,14 +582,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp
QObject - - Reboot - إعادة التشغيل - - - Exit - إغلاق - openpilot openpilot diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 986c0fa54f..3123e52578 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -574,14 +574,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere QObject - - Reboot - Neustart - - - Exit - Verlassen - openpilot openpilot diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index a2d095c064..3d9be63caf 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -574,14 +574,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para QObject - - Reboot - Reiniciar - - - Exit - Salir - openpilot openpilot diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index 673056bf3b..69652510df 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -572,14 +572,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - Reboot - Redémarrer - - - Exit - Quitter - openpilot openpilot diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index da99fdd6e7..4cce79757f 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -572,14 +572,6 @@ Firehoseモードを有効にすると、学習データを最大限アップロ QObject - - Reboot - 再起動 - - - Exit - 閉じる - openpilot openpilot diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 1d8d76f219..3be96846a7 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -572,14 +572,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - Reboot - 재부팅 - - - Exit - 종료 - openpilot 오픈파일럿 diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 4971b7fbfa..a6a473bc92 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -574,14 +574,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar QObject - - Reboot - Reiniciar - - - Exit - Sair - openpilot openpilot diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index aab2270a01..c7521082c3 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -572,14 +572,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - Reboot - รีบูต - - - Exit - ปิด - openpilot openpilot diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 0b0e24dcf4..b8b7202898 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -569,14 +569,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - Reboot - Yeniden başlat - - - Exit - Çık - openpilot openpilot diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 963946e4da..c310265a1c 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -572,14 +572,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - Reboot - 重启 - - - Exit - 退出 - openpilot openpilot diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 1946493247..5f81cc2c53 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -572,14 +572,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp QObject - - Reboot - 重新啟動 - - - Exit - 離開 - openpilot openpilot From 124198460b87e8e3d03751fca983adec9a83b5ea Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 25 Apr 2025 23:33:32 +0100 Subject: [PATCH 075/477] ui(raylib): create BaseWindow (#35074) * ui(raylib): create BaseWindow * test without typing * revert * Revert "test without typing" This reverts commit c8a5e1b8322bb7250ede540d7b6d25eefa72cef1. * lines --- system/ui/lib/window.py | 57 +++++++++++++++++++++++++++++++++++++++++ system/ui/spinner.py | 42 ++++-------------------------- system/ui/text.py | 45 ++++---------------------------- 3 files changed, 67 insertions(+), 77 deletions(-) create mode 100644 system/ui/lib/window.py diff --git a/system/ui/lib/window.py b/system/ui/lib/window.py new file mode 100644 index 0000000000..d039d80b80 --- /dev/null +++ b/system/ui/lib/window.py @@ -0,0 +1,57 @@ +import threading +import time +import os +from typing import Generic, Protocol, TypeVar +from openpilot.system.ui.lib.application import gui_app + + +class RendererProtocol(Protocol): + def render(self): ... + + +R = TypeVar("R", bound=RendererProtocol) + + +class BaseWindow(Generic[R]): + def __init__(self, title: str): + self._title = title + self._renderer: R | None = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run) + self._thread.start() + + # wait for the renderer to be initialized + while self._renderer is None and self._thread.is_alive(): + time.sleep(0.01) + + def _create_renderer(self) -> R: + raise NotImplementedError("Subclasses of BaseWindow must implement _create_renderer()") + + def _run(self): + if os.getenv("CI") is not None: + return + gui_app.init_window("Spinner") + self._renderer = self._create_renderer() + try: + for _ in gui_app.render(): + if self._stop_event.is_set(): + break + self._renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def close(self): + if self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + print(f"WARNING: failed to join {self._title} thread") + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 5da96c73f9..119bdba3e7 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -6,6 +6,7 @@ import time from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.window import BaseWindow from openpilot.system.ui.text import wrap_text # Constants @@ -85,16 +86,12 @@ class SpinnerRenderer: FONT_SIZE, 0.0, rl.WHITE) -class Spinner: +class Spinner(BaseWindow[SpinnerRenderer]): def __init__(self): - self._renderer: SpinnerRenderer | None = None - self._stop_event = threading.Event() - self._thread = threading.Thread(target=self._run) - self._thread.start() + super().__init__("Spinner") - # wait for the renderer to be initialized - while self._renderer is None and self._thread.is_alive(): - time.sleep(0.01) + def _create_renderer(self): + return SpinnerRenderer() def update(self, spinner_text: str): if self._renderer is not None: @@ -103,35 +100,6 @@ class Spinner: def update_progress(self, cur: float, total: float): self.update(str(round(100 * cur / total))) - def _run(self): - if os.getenv("CI") is not None: - return - gui_app.init_window("Spinner") - self._renderer = renderer = SpinnerRenderer() - try: - for _ in gui_app.render(): - if self._stop_event.is_set(): - break - renderer.render() - finally: - gui_app.close() - - def __enter__(self): - return self - - def close(self): - if self._thread.is_alive(): - self._stop_event.set() - self._thread.join(timeout=2.0) - if self._thread.is_alive(): - print("WARNING: failed to join spinner thread") - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - if __name__ == "__main__": with Spinner() as s: diff --git a/system/ui/text.py b/system/ui/text.py index e57ae45d9a..33e8167c64 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,13 +1,12 @@ #!/usr/bin/env python3 -import os import re -import threading import time import pyray as rl from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.window import BaseWindow MARGIN = 50 SPACING = 40 @@ -74,52 +73,18 @@ class TextWindowRenderer: return ret -class TextWindow: +class TextWindow(BaseWindow[TextWindowRenderer]): def __init__(self, text: str): self._text = text + super().__init__("Text") - self._renderer: TextWindowRenderer | None = None - self._stop_event = threading.Event() - self._thread = threading.Thread(target=self._run) - self._thread.start() - - # wait for the renderer to be initialized - while self._renderer is None and self._thread.is_alive(): - time.sleep(0.01) + def _create_renderer(self): + return TextWindowRenderer(self._text) def wait_for_exit(self): while self._thread.is_alive(): time.sleep(0.01) - def _run(self): - if os.getenv("CI") is not None: - return - gui_app.init_window("Text") - self._renderer = renderer = TextWindowRenderer(self._text) - try: - for _ in gui_app.render(): - if self._stop_event.is_set(): - break - renderer.render() - finally: - gui_app.close() - - def __enter__(self): - return self - - def close(self): - if self._thread.is_alive(): - self._stop_event.set() - self._thread.join(timeout=2.0) - if self._thread.is_alive(): - print("WARNING: failed to join text window thread") - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - if __name__ == "__main__": with TextWindow(DEMO_TEXT): From 2619732d9f3ab5b7191df39e09ed4c768f898453 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 25 Apr 2025 18:49:09 -0500 Subject: [PATCH 076/477] Fix tethering connection disabling internet (#35075) * fix * this is ignored in this networkmanager * whoop --- selfdrive/ui/qt/network/wifi_manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/qt/network/wifi_manager.cc b/selfdrive/ui/qt/network/wifi_manager.cc index 1717210634..3d4bb9e7d1 100644 --- a/selfdrive/ui/qt/network/wifi_manager.cc +++ b/selfdrive/ui/qt/network/wifi_manager.cc @@ -439,7 +439,7 @@ void WifiManager::addTetheringConnection() { address["prefix"] = 24u; connection["ipv4"]["address-data"] = QVariant::fromValue(IpConfig() << address); connection["ipv4"]["gateway"] = "192.168.43.1"; - connection["ipv4"]["route-metric"] = 1100; + connection["ipv4"]["never-default"] = true; connection["ipv6"]["method"] = "ignore"; asyncCall(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "AddConnection", QVariant::fromValue(connection)); From 83b84a5bec719c3e8cc1501d56344d95d4a9c4f4 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 26 Apr 2025 01:20:58 +0100 Subject: [PATCH 077/477] Revert "ui(raylib): create BaseWindow (#35074)" This reverts commit 124198460b87e8e3d03751fca983adec9a83b5ea. --- system/ui/lib/window.py | 57 ----------------------------------------- system/ui/spinner.py | 42 ++++++++++++++++++++++++++---- system/ui/text.py | 45 ++++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 67 deletions(-) delete mode 100644 system/ui/lib/window.py diff --git a/system/ui/lib/window.py b/system/ui/lib/window.py deleted file mode 100644 index d039d80b80..0000000000 --- a/system/ui/lib/window.py +++ /dev/null @@ -1,57 +0,0 @@ -import threading -import time -import os -from typing import Generic, Protocol, TypeVar -from openpilot.system.ui.lib.application import gui_app - - -class RendererProtocol(Protocol): - def render(self): ... - - -R = TypeVar("R", bound=RendererProtocol) - - -class BaseWindow(Generic[R]): - def __init__(self, title: str): - self._title = title - self._renderer: R | None = None - self._stop_event = threading.Event() - self._thread = threading.Thread(target=self._run) - self._thread.start() - - # wait for the renderer to be initialized - while self._renderer is None and self._thread.is_alive(): - time.sleep(0.01) - - def _create_renderer(self) -> R: - raise NotImplementedError("Subclasses of BaseWindow must implement _create_renderer()") - - def _run(self): - if os.getenv("CI") is not None: - return - gui_app.init_window("Spinner") - self._renderer = self._create_renderer() - try: - for _ in gui_app.render(): - if self._stop_event.is_set(): - break - self._renderer.render() - finally: - gui_app.close() - - def __enter__(self): - return self - - def close(self): - if self._thread.is_alive(): - self._stop_event.set() - self._thread.join(timeout=2.0) - if self._thread.is_alive(): - print(f"WARNING: failed to join {self._title} thread") - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 119bdba3e7..5da96c73f9 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -6,7 +6,6 @@ import time from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.window import BaseWindow from openpilot.system.ui.text import wrap_text # Constants @@ -86,12 +85,16 @@ class SpinnerRenderer: FONT_SIZE, 0.0, rl.WHITE) -class Spinner(BaseWindow[SpinnerRenderer]): +class Spinner: def __init__(self): - super().__init__("Spinner") + self._renderer: SpinnerRenderer | None = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run) + self._thread.start() - def _create_renderer(self): - return SpinnerRenderer() + # wait for the renderer to be initialized + while self._renderer is None and self._thread.is_alive(): + time.sleep(0.01) def update(self, spinner_text: str): if self._renderer is not None: @@ -100,6 +103,35 @@ class Spinner(BaseWindow[SpinnerRenderer]): def update_progress(self, cur: float, total: float): self.update(str(round(100 * cur / total))) + def _run(self): + if os.getenv("CI") is not None: + return + gui_app.init_window("Spinner") + self._renderer = renderer = SpinnerRenderer() + try: + for _ in gui_app.render(): + if self._stop_event.is_set(): + break + renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def close(self): + if self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + print("WARNING: failed to join spinner thread") + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + if __name__ == "__main__": with Spinner() as s: diff --git a/system/ui/text.py b/system/ui/text.py index 33e8167c64..e57ae45d9a 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 +import os import re +import threading import time import pyray as rl from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.window import BaseWindow MARGIN = 50 SPACING = 40 @@ -73,18 +74,52 @@ class TextWindowRenderer: return ret -class TextWindow(BaseWindow[TextWindowRenderer]): +class TextWindow: def __init__(self, text: str): self._text = text - super().__init__("Text") - def _create_renderer(self): - return TextWindowRenderer(self._text) + self._renderer: TextWindowRenderer | None = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run) + self._thread.start() + + # wait for the renderer to be initialized + while self._renderer is None and self._thread.is_alive(): + time.sleep(0.01) def wait_for_exit(self): while self._thread.is_alive(): time.sleep(0.01) + def _run(self): + if os.getenv("CI") is not None: + return + gui_app.init_window("Text") + self._renderer = renderer = TextWindowRenderer(self._text) + try: + for _ in gui_app.render(): + if self._stop_event.is_set(): + break + renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def close(self): + if self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + print("WARNING: failed to join text window thread") + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + if __name__ == "__main__": with TextWindow(DEMO_TEXT): From 9d17c73f6b8677fc986d2c02096950715c64694c Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 26 Apr 2025 03:09:19 +0100 Subject: [PATCH 078/477] avoid importing Raylib UI (#35078) --- system/athena/registration.py | 2 +- system/manager/manager.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/system/athena/registration.py b/system/athena/registration.py index e48ae64aea..ce7fcea89f 100755 --- a/system/athena/registration.py +++ b/system/athena/registration.py @@ -10,7 +10,6 @@ from openpilot.common.params import Params from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.system.hardware import HARDWARE, PC from openpilot.system.hardware.hw import Paths -from openpilot.system.ui.spinner import Spinner from openpilot.common.swaglog import cloudlog @@ -45,6 +44,7 @@ def register(show_spinner=False) -> str | None: cloudlog.warning(f"missing public key: {pubkey}") elif dongle_id is None: if show_spinner: + from openpilot.system.ui.spinner import Spinner spinner = Spinner() spinner.update("registering device") diff --git a/system/manager/manager.py b/system/manager/manager.py index d8c56cbc73..c3ffe28457 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -17,7 +17,6 @@ from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_I from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.system.version import get_build_metadata, terms_version, training_version from openpilot.system.hardware.hw import Paths -from openpilot.system.ui.text import TextWindow def manager_init() -> None: @@ -203,6 +202,8 @@ def main() -> None: if __name__ == "__main__": + from openpilot.system.ui.text import TextWindow + unblock_stdout() try: From e7cf508f500cbd6cd783554c37185d87765e848c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Sat, 26 Apr 2025 07:30:47 -0700 Subject: [PATCH 079/477] Tomb Raider 5 (#35080) 846c53a4-b886-4942-a4d3-7ea3aff5be8d/400 --- selfdrive/modeld/models/driving_policy.onnx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 528dd298d4..2075792f4d 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe85c824c5c41580eb3818400433377b0bfc405927f8cb7d88af9a836bed693d +oid sha256:b86d130c5db2d772da1b139c136ed86976f37137129a19a6b881fdf641bca198 size 15578328 From be168d14e9e402237a01178766af0be8c9b8d4e4 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 26 Apr 2025 19:33:35 +0200 Subject: [PATCH 080/477] SL: updating localproxy implementation (#841) * Adding capabilities to route localProxy via sunnylink * Undo * Thx lint * get api token * cert is not valid when it's an IP. Still use cert, but don't validate --- sunnypilot/sunnylink/athena/sunnylinkd.py | 17 ++++++++++++++-- sunnypilot/sunnylink/utils.py | 9 +++++++++ system/athena/athenad.py | 24 +++++++++++++---------- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/sunnypilot/sunnylink/athena/sunnylinkd.py b/sunnypilot/sunnylink/athena/sunnylinkd.py index 2c4b95fa46..a675820628 100755 --- a/sunnypilot/sunnylink/athena/sunnylinkd.py +++ b/sunnypilot/sunnylink/athena/sunnylinkd.py @@ -10,11 +10,12 @@ import threading import time from jsonrpc import dispatcher +from functools import partial from openpilot.common.params import Params from openpilot.common.realtime import set_core_affinity from openpilot.common.swaglog import cloudlog from openpilot.system.athena.athenad import ws_send, jsonrpc_handler, \ - recv_queue, UploadQueueCache, upload_queue, cur_upload_items, backoff, ws_manage, log_handler + recv_queue, UploadQueueCache, upload_queue, cur_upload_items, backoff, ws_manage, log_handler, start_local_proxy_shim from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutException, create_connection) @@ -50,7 +51,7 @@ def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None: # threading.Thread(target=sunny_log_handler, args=(end_event, comma_prime_cellular_end_event), name='log_handler'), # threading.Thread(target=stat_handler, args=(end_event,), name='stat_handler'), ] + [ - threading.Thread(target=jsonrpc_handler, args=(end_event,), name=f'worker_{x}') + threading.Thread(target=jsonrpc_handler, args=(end_event, partial(startLocalProxy, end_event),), name=f'worker_{x}') for x in range(HANDLER_THREADS) ] @@ -201,6 +202,18 @@ def saveParams(params_to_update: dict[str, str], compression: bool = False) -> N cloudlog.error(f"sunnylinkd.saveParams.exception {e}") +def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> dict[str, int]: + cloudlog.debug("athena.startLocalProxy.starting") + ws = create_connection( + remote_ws_uri, + header={"Authorization": f"Bearer {sunnylink_api.get_token()}"}, + enable_multithread=True, + sslopt={"cert_reqs": ssl.CERT_NONE} + ) + + return start_local_proxy_shim(global_end_event, local_port, ws) + + def main(exit_event: threading.Event = None): try: set_core_affinity([0, 1, 2, 3]) diff --git a/sunnypilot/sunnylink/utils.py b/sunnypilot/sunnylink/utils.py index 57523d7394..55b1f6f285 100644 --- a/sunnypilot/sunnylink/utils.py +++ b/sunnypilot/sunnylink/utils.py @@ -46,3 +46,12 @@ def register_sunnylink(): sunnylink_id = SunnylinkApi(None).register_device(None, **extra_args) print(f"SunnyLinkId: {sunnylink_id}") + + +def get_api_token(): + """Get the API token for the device.""" + params = Params() + sunnylink_dongle_id = params.get("SunnylinkDongleId", encoding='utf-8') + sunnylink_api = SunnylinkApi(sunnylink_dongle_id) + token = sunnylink_api.get_token() + print(f"API Token: {token}") diff --git a/system/athena/athenad.py b/system/athena/athenad.py index 4bcdc9b83c..3f3fc3d70d 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -184,8 +184,8 @@ def handle_long_poll(ws: WebSocket, exit_event: threading.Event | None) -> None: thread.join() -def jsonrpc_handler(end_event: threading.Event) -> None: - dispatcher["startLocalProxy"] = partial(startLocalProxy, end_event) +def jsonrpc_handler(end_event: threading.Event, localProxyHandler = None) -> None: + dispatcher["startLocalProxy"] = localProxyHandler or partial(startLocalProxy, end_event) while not end_event.is_set(): try: data = recv_queue.get(timeout=1) @@ -480,7 +480,19 @@ def setRouteViewed(route: str) -> dict[str, int | str]: def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local_port: int) -> dict[str, int]: + cloudlog.debug("athena.startLocalProxy.starting") + dongle_id = Params().get("DongleId").decode('utf8') + identity_token = Api(dongle_id).get_token() + ws = create_connection(remote_ws_uri, cookie="jwt=" + identity_token, enable_multithread=True) + + return start_local_proxy_shim(global_end_event, local_port, ws) + + +def start_local_proxy_shim(global_end_event: threading.Event, local_port: int, ws: WebSocket) -> dict[str, int]: try: + if ws.sock is None: + raise Exception("WebSocket is not connected") + # migration, can be removed once 0.9.8 is out for a while if local_port == 8022: local_port = 22 @@ -488,14 +500,6 @@ def startLocalProxy(global_end_event: threading.Event, remote_ws_uri: str, local if local_port not in LOCAL_PORT_WHITELIST: raise Exception("Requested local port not whitelisted") - cloudlog.debug("athena.startLocalProxy.starting") - - dongle_id = Params().get("DongleId").decode('utf8') - identity_token = Api(dongle_id).get_token() - ws = create_connection(remote_ws_uri, - cookie="jwt=" + identity_token, - enable_multithread=True) - # Set TOS to keep connection responsive while under load. # DSCP of 36/HDD_LINUX_AC_VI with the minimum delay flag ws.sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, 0x90) From c0b0bb554cb4b9bebfad64a2d1cf89dea3dd6964 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Sat, 26 Apr 2025 13:43:18 -0700 Subject: [PATCH 081/477] fix(replay): search for route key in entire str (#35081) fix(replay): look at whole segment string --- tools/replay/route.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/route.cc b/tools/replay/route.cc index 7731d0daf4..ff5a858e8c 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -121,7 +121,7 @@ bool Route::loadFromJson(const std::string &json) { bool Route::loadFromLocal() { std::string pattern = route_.timestamp + "--"; for (const auto &entry : std::filesystem::directory_iterator(data_dir_)) { - if (entry.is_directory() && entry.path().filename().string().find(pattern) == 0) { + if (entry.is_directory() && entry.path().filename().string().find(pattern) != std::string::npos) { std::string segment = entry.path().string(); int seg_num = std::atoi(segment.substr(segment.rfind("--") + 2).c_str()); From c4acae6b51983bcb6c4d2dc0f8aeec9401312888 Mon Sep 17 00:00:00 2001 From: William Bonilla <47262366+WilliamBonilla62@users.noreply.github.com> Date: Sun, 27 Apr 2025 22:54:00 -0400 Subject: [PATCH 082/477] =?UTF-8?q?=20"docs:=20update=20'Turn=20the=20Spee?= =?UTF-8?q?d=20Blue'=20tutorial=20with=20new=20UI=20path=20and=20fu?= =?UTF-8?q?=E2=80=A6=20(#35016)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * "docs: update 'Turn the Speed Blue' tutorial with new UI path and function" * Change 'Openpilot' to all lowercase ('openpilot') based on the review. --- docs/how-to/turn-the-speed-blue.md | 84 ++++++++++++++++++------------ 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/docs/how-to/turn-the-speed-blue.md b/docs/how-to/turn-the-speed-blue.md index e8b55ef81b..64f4475dfa 100644 --- a/docs/how-to/turn-the-speed-blue.md +++ b/docs/how-to/turn-the-speed-blue.md @@ -20,7 +20,7 @@ source .venv/bin/activate Then, compile openpilot: ```bash -scons -j8 +scons -j$(nproc) ``` ## 2. Run replay @@ -38,61 +38,77 @@ The openpilot UI should launch and show a replay of the demo route. If you have your own comma device, you can replace `--demo` with one of your own routes from comma connect. + ## 3. Make the speed blue -Search for “mph” with git grep in the `ui` folder. +Now let’s update the speed display color in the UI. + +Search for the function responsible for rendering UI text: ```bash -$ git grep "mph" selfdrive/ui/ -paint.cc: ui_draw_text(s, s->fb_w/2, 290, s->scene.is_metric ? "km/h" : "mph", 36 * 2.5, COLOR_WHITE_ALPHA(200), "sans-regular"); +git grep "drawText" selfdrive/ui/qt/onroad/hud.cc ``` -The line right above contains the actual speed. Unfortunately, COLOR_BLUE isn’t defined, but a git grep of COLOR_WHITE shows it’s nvgRGBA(255, 255, 255, 255). Personally, I like a lighter blue, so I went with #8080FF. +You’ll find the relevant code inside `selfdrive/ui/qt/onroad/hud.cc`, in this function: + +```cpp +void HudRenderer::drawText(QPainter &p, int x, int y, const QString &text, int alpha) { + QRect real_rect = p.fontMetrics().boundingRect(text); + real_rect.moveCenter({x, y - real_rect.height() / 2}); + + p.setPen(QColor(0xff, 0xff, 0xff, alpha)); // <- this sets the speed text color + p.drawText(real_rect.x(), real_rect.bottom(), text); +} +``` + +Change the `QColor(...)` line to make it **blue** instead of white. A nice soft blue is `#8080FF`, which translates to: + +```diff +- p.setPen(QColor(0xff, 0xff, 0xff, alpha)); ++ p.setPen(QColor(0x80, 0x80, 0xFF, alpha)); +``` + +This change will tint all speed-related UI text to blue with the same transparency (`alpha`). + +--- + +## 4. Rebuild the UI + +After making changes, rebuild Openpilot so your new UI is compiled: ```bash -$ git diff -diff --git a/selfdrive/ui/paint.cc b/selfdrive/ui/paint.cc -index 821d95115..cc996eaa1 100644 ---- a/selfdrive/ui/paint.cc -+++ b/selfdrive/ui/paint.cc -@@ -175,8 +175,8 @@ static void ui_draw_vision_speed(UIState *s) { - const float speed = std::max(0.0, (*s->sm)["carState"].getCarState().getVEgo() * (s->scene.is_metric ? 3.6 : 2.2369363)); - const std::string speed_str = std::to_string((int)std::nearbyint(speed)); - nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); -- ui_draw_text(s, s->fb_w/2, 210, speed_str.c_str(), 96 * 2.5, COLOR_WHITE, "sans-bold"); -- ui_draw_text(s, s->fb_w/2, 290, s->scene.is_metric ? "km/h" : "mph", 36 * 2.5, COLOR_WHITE_ALPHA(200), "sans-regular"); -+ ui_draw_text(s, s->fb_w/2, 210, speed_str.c_str(), 96 * 2.5, nvgRGBA(128, 128, 255, 255), "sans-bold"); -+ ui_draw_text(s, s->fb_w/2, 290, s->scene.is_metric ? "km/h" : "mph", 36 * 2.5, nvgRGBA(128, 128, 255, 200), "sans-regular"); - } - - static void ui_draw_vision_event(UIState *s) { +scons -j$(nproc) && selfdrive/ui/ui ``` - - -## 4. Rebuild UI, and admire your work - -``` -scons -j8 && selfdrive/ui/ui -``` - ![](https://blog.comma.ai/img/blue_speed_ui.png) +You should now see the speed displayed in a nice blue shade during the demo replay. + +--- + ## 5. Push your fork to GitHub -Click fork on GitHub. Then, push with: +Click **"Fork"** on the [Openpilot GitHub repo](https://github.com/commaai/openpilot). Then push with: ```bash git remote rm origin git remote add origin git@github.com:/openpilot.git git add . -git commit -m "Make the speed blue." +git commit -m "Make the speed display blue" git push --set-upstream origin master ``` -## 6. Run your fork on device in your car! +--- -Uninstall openpilot from your device through the settings. Then, enter the URL for your very own installer: +## 6. Run your fork on your comma device + +Uninstall Openpilot through the settings on your device. + +Then reinstall using your own GitHub-hosted fork: ``` installer.comma.ai//master ``` -## 7. Admire your work IRL +--- + +## 7. Admire your work IRL 🚗💨 + +You’ve now successfully modified Openpilot’s UI and deployed it to your own car! ![](https://blog.comma.ai/img/c3_blue_ui.jpg) From c5b75168e9553ef8893213a36038889db003258c Mon Sep 17 00:00:00 2001 From: William Bonilla <47262366+WilliamBonilla62@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:30:16 -0400 Subject: [PATCH 083/477] Fix test_update_returns_state to flatten Kalman output (#35083) fix(tests): flatten Kalman filter output in test_update_returns_state --- common/tests/test_simple_kalman.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/tests/test_simple_kalman.py b/common/tests/test_simple_kalman.py index f4a967e58a..e44ac2cc57 100644 --- a/common/tests/test_simple_kalman.py +++ b/common/tests/test_simple_kalman.py @@ -24,6 +24,6 @@ class TestSimpleKalman: self.kf.set_x([[1.0], [1.0]]) assert self.kf.x == [[1.0], [1.0]] - def update_returns_state(self): + def test_update_returns_state(self): x = self.kf.update(100) - assert x == self.kf.x + assert x == [i[0] for i in self.kf.x] From 91fbbf812fb935e44369d6899a04b42f02edf950 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:33:28 -0700 Subject: [PATCH 084/477] [bot] Update Python packages (#35085) Update Python packages Co-authored-by: Vehicle Researcher --- docs/CARS.md | 2 +- opendbc_repo | 2 +- uv.lock | 54 ++++++++++++++++++++++++++++------------------------ 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 95af300c0f..c22568ce2e 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -80,7 +80,7 @@ A supported vehicle is one that just works when you install a comma device. All |Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|HR-V 2019-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|HR-V 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|HR-V 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/opendbc_repo b/opendbc_repo index 7bad67af29..b35c84f07c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 7bad67af29ea117ce8cf51cc6549c97b9acdafe1 +Subproject commit b35c84f07cfcd7ef02246f8eba69bbfd6216431c diff --git a/uv.lock b/uv.lock index a8a99e9979..4981010457 100644 --- a/uv.lock +++ b/uv.lock @@ -217,11 +217,11 @@ wheels = [ [[package]] name = "certifi" -version = "2025.1.31" +version = "2025.4.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload_time = "2025-01-31T02:16:47.166Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload_time = "2025-04-26T02:12:29.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload_time = "2025-01-31T02:16:45.015Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload_time = "2025-04-26T02:12:27.662Z" }, ] [[package]] @@ -1632,24 +1632,28 @@ wheels = [ [[package]] name = "pyarrow" -version = "19.0.1" +version = "20.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437, upload_time = "2025-02-18T18:55:57.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187, upload_time = "2025-04-27T12:34:23.264Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/55/f1a8d838ec07fe3ca53edbe76f782df7b9aafd4417080eebf0b42aab0c52/pyarrow-19.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90", size = 30713987, upload_time = "2025-02-18T18:52:20.463Z" }, - { url = "https://files.pythonhosted.org/packages/13/12/428861540bb54c98a140ae858a11f71d041ef9e501e6b7eb965ca7909505/pyarrow-19.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00", size = 32135613, upload_time = "2025-02-18T18:52:25.29Z" }, - { url = "https://files.pythonhosted.org/packages/2f/8a/23d7cc5ae2066c6c736bce1db8ea7bc9ac3ef97ac7e1c1667706c764d2d9/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae", size = 41149147, upload_time = "2025-02-18T18:52:30.975Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7a/845d151bb81a892dfb368bf11db584cf8b216963ccce40a5cf50a2492a18/pyarrow-19.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5", size = 42178045, upload_time = "2025-02-18T18:52:36.859Z" }, - { url = "https://files.pythonhosted.org/packages/a7/31/e7282d79a70816132cf6cae7e378adfccce9ae10352d21c2fecf9d9756dd/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3", size = 40532998, upload_time = "2025-02-18T18:52:42.578Z" }, - { url = "https://files.pythonhosted.org/packages/b8/82/20f3c290d6e705e2ee9c1fa1d5a0869365ee477e1788073d8b548da8b64c/pyarrow-19.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6", size = 42084055, upload_time = "2025-02-18T18:52:48.749Z" }, - { url = "https://files.pythonhosted.org/packages/ff/77/e62aebd343238863f2c9f080ad2ef6ace25c919c6ab383436b5b81cbeef7/pyarrow-19.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466", size = 25283133, upload_time = "2025-02-18T18:52:54.549Z" }, - { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749, upload_time = "2025-02-18T18:53:00.062Z" }, - { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007, upload_time = "2025-02-18T18:53:06.581Z" }, - { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566, upload_time = "2025-02-18T18:53:11.958Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991, upload_time = "2025-02-18T18:53:17.678Z" }, - { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986, upload_time = "2025-02-18T18:53:26.263Z" }, - { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026, upload_time = "2025-02-18T18:53:33.063Z" }, - { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108, upload_time = "2025-02-18T18:53:38.462Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/b7930824181ceadd0c63c1042d01fa4ef63eee233934826a7a2a9af6e463/pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0", size = 30856035, upload_time = "2025-04-27T12:28:40.78Z" }, + { url = "https://files.pythonhosted.org/packages/9b/18/c765770227d7f5bdfa8a69f64b49194352325c66a5c3bb5e332dfd5867d9/pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb", size = 32309552, upload_time = "2025-04-27T12:28:47.051Z" }, + { url = "https://files.pythonhosted.org/packages/44/fb/dfb2dfdd3e488bb14f822d7335653092dde150cffc2da97de6e7500681f9/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232", size = 41334704, upload_time = "2025-04-27T12:28:55.064Z" }, + { url = "https://files.pythonhosted.org/packages/58/0d/08a95878d38808051a953e887332d4a76bc06c6ee04351918ee1155407eb/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f", size = 42399836, upload_time = "2025-04-27T12:29:02.13Z" }, + { url = "https://files.pythonhosted.org/packages/f3/cd/efa271234dfe38f0271561086eedcad7bc0f2ddd1efba423916ff0883684/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab", size = 40711789, upload_time = "2025-04-27T12:29:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/46/1f/7f02009bc7fc8955c391defee5348f510e589a020e4b40ca05edcb847854/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62", size = 42301124, upload_time = "2025-04-27T12:29:17.187Z" }, + { url = "https://files.pythonhosted.org/packages/4f/92/692c562be4504c262089e86757a9048739fe1acb4024f92d39615e7bab3f/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c", size = 42916060, upload_time = "2025-04-27T12:29:24.253Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ec/9f5c7e7c828d8e0a3c7ef50ee62eca38a7de2fa6eb1b8fa43685c9414fef/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3", size = 44547640, upload_time = "2025-04-27T12:29:32.782Z" }, + { url = "https://files.pythonhosted.org/packages/54/96/46613131b4727f10fd2ffa6d0d6f02efcc09a0e7374eff3b5771548aa95b/pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc", size = 25781491, upload_time = "2025-04-27T12:29:38.464Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d6/0c10e0d54f6c13eb464ee9b67a68b8c71bcf2f67760ef5b6fbcddd2ab05f/pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba", size = 30815067, upload_time = "2025-04-27T12:29:44.384Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e2/04e9874abe4094a06fd8b0cbb0f1312d8dd7d707f144c2ec1e5e8f452ffa/pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781", size = 32297128, upload_time = "2025-04-27T12:29:52.038Z" }, + { url = "https://files.pythonhosted.org/packages/31/fd/c565e5dcc906a3b471a83273039cb75cb79aad4a2d4a12f76cc5ae90a4b8/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199", size = 41334890, upload_time = "2025-04-27T12:29:59.452Z" }, + { url = "https://files.pythonhosted.org/packages/af/a9/3bdd799e2c9b20c1ea6dc6fa8e83f29480a97711cf806e823f808c2316ac/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd", size = 42421775, upload_time = "2025-04-27T12:30:06.875Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/da98ccd86354c332f593218101ae56568d5dcedb460e342000bd89c49cc1/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28", size = 40687231, upload_time = "2025-04-27T12:30:13.954Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1b/2168d6050e52ff1e6cefc61d600723870bf569cbf41d13db939c8cf97a16/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8", size = 42295639, upload_time = "2025-04-27T12:30:21.949Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/2d976c0c7158fd25591c8ca55aee026e6d5745a021915a1835578707feb3/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e", size = 42908549, upload_time = "2025-04-27T12:30:29.551Z" }, + { url = "https://files.pythonhosted.org/packages/31/a9/dfb999c2fc6911201dcbf348247f9cc382a8990f9ab45c12eabfd7243a38/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a", size = 44557216, upload_time = "2025-04-27T12:30:36.977Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8e/9adee63dfa3911be2382fb4d92e4b2e7d82610f9d9f668493bebaa2af50f/pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b", size = 25660496, upload_time = "2025-04-27T12:30:42.809Z" }, ] [[package]] @@ -4810,11 +4814,11 @@ wheels = [ [[package]] name = "setuptools" -version = "79.0.1" +version = "80.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/71/b6365e6325b3290e14957b2c3a804a529968c77a049b2ed40c095f749707/setuptools-79.0.1.tar.gz", hash = "sha256:128ce7b8f33c3079fd1b067ecbb4051a66e8526e7b65f6cec075dfc650ddfa88", size = 1367909, upload_time = "2025-04-23T22:20:59.241Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/80/97e25f0f1e4067677806084b7382a6ff9979f3d15119375c475c288db9d7/setuptools-80.0.0.tar.gz", hash = "sha256:c40a5b3729d58dd749c0f08f1a07d134fb8a0a3d7f87dc33e7c5e1f762138650", size = 1354221, upload_time = "2025-04-27T17:21:10.806Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/6d/b4752b044bf94cb802d88a888dc7d288baaf77d7910b7dedda74b5ceea0c/setuptools-79.0.1-py3-none-any.whl", hash = "sha256:e147c0549f27767ba362f9da434eab9c5dc0045d5304feb602a0af001089fc51", size = 1256281, upload_time = "2025-04-23T22:20:56.768Z" }, + { url = "https://files.pythonhosted.org/packages/23/63/5517029d6696ddf2bd378d46f63f479be001c31b462303170a1da57650cb/setuptools-80.0.0-py3-none-any.whl", hash = "sha256:a38f898dcd6e5380f4da4381a87ec90bd0a7eec23d204a5552e80ee3cab6bd27", size = 1240907, upload_time = "2025-04-27T17:21:09.175Z" }, ] [[package]] @@ -4914,14 +4918,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/401c0a7be089e0282 [[package]] name = "sympy" -version = "1.13.3" +version = "1.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/8a/5a7fd6284fa8caac23a26c9ddf9c30485a48169344b4bd3b0f02fef1890f/sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9", size = 7533196, upload_time = "2024-09-18T21:54:25.591Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload_time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73", size = 6189483, upload_time = "2024-09-18T21:54:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload_time = "2025-04-27T18:04:59.103Z" }, ] [[package]] From cf0b0399d95cf591579ecd3ea6b61c533a3baf6e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 29 Apr 2025 15:48:29 -0700 Subject: [PATCH 085/477] replay: more speed options (#35091) * match connect * simple --- tools/replay/consoleui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/consoleui.h b/tools/replay/consoleui.h index 3d4abeb458..7731f8017f 100644 --- a/tools/replay/consoleui.h +++ b/tools/replay/consoleui.h @@ -12,7 +12,7 @@ public: ConsoleUI(Replay *replay); ~ConsoleUI(); int exec(); - inline static const std::array speed_array = {0.2f, 0.5f, 1.0f, 2.0f, 3.0f}; + inline static const std::array speed_array = {0.2f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f}; private: void initWindows(); From a0bcea5719ce4fb3d335fea841d335c284c11ccd Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 29 Apr 2025 15:49:41 -0700 Subject: [PATCH 086/477] capitalization typo --- tools/replay/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/replay/README.md b/tools/replay/README.md index f02090c58e..719f7d2db9 100644 --- a/tools/replay/README.md +++ b/tools/replay/README.md @@ -81,7 +81,7 @@ Arguments: connect.comma.ai ``` -## Visualize the Replay in the Openpilot UI +## Visualize the Replay in the openpilot UI To visualize the replay within the openpilot UI, run the following commands: ```bash From 45787163a225c459234433118aeaba83c1a610ef Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 30 Apr 2025 15:32:11 -0700 Subject: [PATCH 087/477] feat: clip.py (#35071) --- tools/clip/run.py | 215 +++++++++++++++++++++++++++ tools/install_ubuntu_dependencies.sh | 3 +- tools/op.sh | 7 + 3 files changed, 224 insertions(+), 1 deletion(-) create mode 100755 tools/clip/run.py diff --git a/tools/clip/run.py b/tools/clip/run.py new file mode 100755 index 0000000000..97baa880ea --- /dev/null +++ b/tools/clip/run.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 + +import atexit +import logging +import os +import platform +import shutil +import time +from argparse import ArgumentParser, ArgumentTypeError +from collections.abc import Sequence +from pathlib import Path +from random import randint +from subprocess import Popen, PIPE +from typing import Literal + +from cereal.messaging import SubMaster +from openpilot.common.basedir import BASEDIR +from openpilot.common.prefix import OpenpilotPrefix +from openpilot.tools.lib.route import SegmentRange, get_max_seg_number_cached + +DEFAULT_OUTPUT = 'output.mp4' +DEMO_START = 90 +DEMO_END = 105 +DEMO_ROUTE = 'a2a0ccea32023010/2023-07-27--13-01-19' +FRAMERATE = 20 +PIXEL_DEPTH = '24' +RESOLUTION = '2160x1080' +SECONDS_TO_WARM = 2 +PROC_WAIT_SECONDS = 5 + +REPLAY = str(Path(BASEDIR, 'tools/replay/replay').resolve()) +UI = str(Path(BASEDIR, 'selfdrive/ui/ui').resolve()) + +logger = logging.getLogger('clip.py') + + +def check_for_failure(proc: Popen): + exit_code = proc.poll() + if exit_code is not None and exit_code != 0: + cmd = str(proc.args) + if isinstance(proc.args, str): + cmd = proc.args + elif isinstance(proc.args, Sequence): + cmd = str(proc.args[0]) + msg = f'{cmd} failed, exit code {exit_code}' + logger.error(msg) + stdout, stderr = proc.communicate() + if stdout: + logger.error(stdout.decode()) + if stderr: + logger.error(stderr.decode()) + raise ChildProcessError(msg) + + +def parse_args(parser: ArgumentParser): + args = parser.parse_args() + if args.demo: + args.route = DEMO_ROUTE + if args.start is None or args.end is None: + args.start = DEMO_START + args.end = DEMO_END + elif args.route.count('/') == 1: + if args.start is None or args.end is None: + parser.error('must provide both start and end if timing is not in the route ID') + elif args.route.count('/') == 3: + if args.start is not None or args.end is not None: + parser.error('don\'t provide timing when including it in the route ID') + parts = args.route.split('/') + args.route = '/'.join(parts[:2]) + args.start = int(parts[2]) + args.end = int(parts[3]) + if args.end <= args.start: + parser.error(f'end ({args.end}) must be greater than start ({args.start})') + if args.start < SECONDS_TO_WARM: + parser.error(f'start must be greater than {SECONDS_TO_WARM}s to allow the UI time to warm up') + + # if using local files, don't worry about length check right now so we skip the network call + # TODO: derive segment count from local FS + if not args.data_dir: + try: + num_segs = get_max_seg_number_cached(SegmentRange(args.route)) + except Exception as e: + parser.error(f'failed to get route length: {e}') + + # FIXME: length isn't exactly max segment seconds, simplify to replay exiting at end of data + length = round(num_segs * 60) + if args.start >= length: + parser.error(f'start ({args.start}s) cannot be after end of route ({length}s)') + if args.end > length: + parser.error(f'end ({args.end}s) cannot be after end of route ({length}s)') + + return args + + +def start_proc(args: list[str], env: dict[str, str]): + return Popen(args, env=env, stdout=PIPE, stderr=PIPE) + + +def validate_env(parser: ArgumentParser): + if platform.system() not in ['Linux']: + parser.exit(1, f'clip.py: error: {platform.system()} is not a supported operating system\n') + for proc in ['Xvfb', 'ffmpeg']: + if shutil.which(proc) is None: + parser.exit(1, f'clip.py: error: missing {proc} command, is it installed?\n') + for proc in [REPLAY, UI]: + if shutil.which(proc) is None: + parser.exit(1, f'clip.py: error: missing {proc} command, did you build openpilot yet?\n') + + +def validate_output_file(output_file: str): + if not output_file.endswith('.mp4'): + raise ArgumentTypeError('output must be an mp4') + return output_file + + +def validate_route(route: str): + if route.count('/') not in (1, 3): + raise ArgumentTypeError(f'route must include or exclude timing, example: {DEMO_ROUTE}') + return route + + +def wait_for_frames(procs: list[Popen]): + sm = SubMaster(['uiDebug']) + no_frames_drawn = True + while no_frames_drawn: + sm.update() + no_frames_drawn = sm['uiDebug'].drawTimeMillis == 0. + for proc in procs: + check_for_failure(proc) + + +def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, route: str, output_filepath: str, start: int, end: int, target_size_mb: int): + logger.info(f'clipping route {route}, start={start} end={end} quality={quality} target_filesize={target_size_mb}MB') + + begin_at = max(start - SECONDS_TO_WARM, 0) + duration = end - start + bit_rate_kbps = int(round(target_size_mb * 8 * 1024 * 1024 / duration / 1000)) + + # TODO: evaluate creating fn that inspects /tmp/.X11-unix and creates unused display to avoid possibility of collision + display = f':{randint(99, 999)}' + + ffmpeg_cmd = [ + 'ffmpeg', '-y', '-video_size', RESOLUTION, '-framerate', str(FRAMERATE), '-f', 'x11grab', '-draw_mouse', '0', + '-i', display, '-c:v', 'libx264', '-maxrate', f'{bit_rate_kbps}k', '-bufsize', f'{bit_rate_kbps*2}k', '-crf', '23', + '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', '-f', 'mp4', '-t', str(duration), output_filepath, + ] + + replay_cmd = [REPLAY, '-c', '1', '-s', str(begin_at), '--prefix', prefix] + if data_dir: + replay_cmd.extend(['--data_dir', data_dir]) + if quality == 'low': + replay_cmd.append('--qcam') + replay_cmd.append(route) + + ui_cmd = [UI, '-platform', 'xcb'] + xvfb_cmd = ['Xvfb', display, '-terminate', '-screen', '0', f'{RESOLUTION}x{PIXEL_DEPTH}'] + + with OpenpilotPrefix(prefix, shared_download_cache=True): + env = os.environ.copy() + env['DISPLAY'] = display + + xvfb_proc = start_proc(xvfb_cmd, env) + atexit.register(lambda: xvfb_proc.terminate()) + ui_proc = start_proc(ui_cmd, env) + atexit.register(lambda: ui_proc.terminate()) + replay_proc = start_proc(replay_cmd, env) + atexit.register(lambda: replay_proc.terminate()) + procs = [replay_proc, ui_proc, xvfb_proc] + + logger.info('waiting for replay to begin (loading segments, may take a while)...') + wait_for_frames(procs) + + logger.debug(f'letting UI warm up ({SECONDS_TO_WARM}s)...') + time.sleep(SECONDS_TO_WARM) + for proc in procs: + check_for_failure(proc) + + ffmpeg_proc = start_proc(ffmpeg_cmd, env) + procs.append(ffmpeg_proc) + atexit.register(lambda: ffmpeg_proc.terminate()) + + logger.info(f'recording in progress ({duration}s)...') + ffmpeg_proc.wait(duration + PROC_WAIT_SECONDS) + for proc in procs: + check_for_failure(proc) + logger.info(f'recording complete: {Path(output_filepath).resolve()}') + + +def main(): + p = ArgumentParser(prog='clip.py', description='clip your openpilot route.', epilog='comma.ai') + validate_env(p) + route_group = p.add_mutually_exclusive_group(required=True) + route_group.add_argument('route', nargs='?', type=validate_route, help=f'The route (e.g. {DEMO_ROUTE} or {DEMO_ROUTE}/{DEMO_START}/{DEMO_END})') + route_group.add_argument('--demo', help='use the demo route', action='store_true') + p.add_argument('-d', '--data-dir', help='local directory where route data is stored') + p.add_argument('-e', '--end', help='stop clipping at seconds', type=int) + p.add_argument('-f', '--file-size', help='target file size (Discord/GitHub support max 10MB, default is 9MB)', type=float, default=9.) + p.add_argument('-o', '--output', help='output clip to (.mp4)', type=validate_output_file, default=DEFAULT_OUTPUT) + p.add_argument('-p', '--prefix', help='openpilot prefix', default=f'clip_{randint(100, 99999)}') + p.add_argument('-q', '--quality', help='quality of camera (low = qcam, high = hevc)', choices=['low', 'high'], default='high') + p.add_argument('-s', '--start', help='start clipping at seconds', type=int) + args = parse_args(p) + try: + clip(args.data_dir, args.quality, args.prefix, args.route, args.output, args.start, args.end, args.file_size) + except KeyboardInterrupt as e: + logger.exception('interrupted by user', exc_info=e) + except Exception as e: + logger.exception('encountered error', exc_info=e) + finally: + atexit._run_exitfuncs() + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s %(levelname)s\t%(message)s') + main() diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index 03302364e9..f33569704a 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -63,7 +63,8 @@ function install_ubuntu_common_requirements() { libqt5svg5-dev \ libqt5serialbus5-dev \ libqt5x11extras5-dev \ - libqt5opengl5-dev + libqt5opengl5-dev \ + xvfb } # Install Ubuntu 24.04 LTS packages diff --git a/tools/op.sh b/tools/op.sh index b4508f7ef7..8a8faaf237 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -328,6 +328,11 @@ function op_sim() { op_run_command exec tools/sim/launch_openpilot.sh } +function op_clip() { + op_before_cmd + op_run_command tools/clip/run.py $@ +} + function op_switch() { REMOTE="origin" if [ "$#" -gt 1 ]; then @@ -395,6 +400,7 @@ function op_default() { echo -e " ${BOLD}juggle${NC} Run PlotJuggler" echo -e " ${BOLD}replay${NC} Run Replay" echo -e " ${BOLD}cabana${NC} Run Cabana" + echo -e " ${BOLD}clip${NC} Run clip (linux only)" echo -e " ${BOLD}adb${NC} Run adb shell" echo "" echo -e "${BOLD}${UNDERLINE}Commands [Testing]:${NC}" @@ -445,6 +451,7 @@ function _op() { lint ) shift 1; op_lint "$@" ;; test ) shift 1; op_test "$@" ;; replay ) shift 1; op_replay "$@" ;; + clip ) shift 1; op_clip "$@" ;; sim ) shift 1; op_sim "$@" ;; install ) shift 1; op_install "$@" ;; switch ) shift 1; op_switch "$@" ;; From c5ba5c9c23595e35a2282b79b6a399a1686edf7a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 30 Apr 2025 15:44:27 -0700 Subject: [PATCH 088/477] push back a couple weeks, cvpr release! --- RELEASES.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index ced7e3d4fc..3ff80ca4dc 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,10 +1,12 @@ -Version 0.9.9 (2025-04-30) +Version 0.9.9 (2025-05-15) ======================== * New driving model + * New training architecture supervised by MLSIM +* Steering actuator delay is now learned online * Tesla Model 3 and Y support thanks to lukasloetkolben! * Coming soon - * New driving model supervised by MLSIM - * An online learner for steering actuator delay + * New Honda models + * Bigger vision model Version 0.9.8 (2025-02-28) ======================== From 9f1e462faabdc000b5c574b0e38818bcd6068e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Wed, 30 Apr 2025 17:19:53 -0700 Subject: [PATCH 089/477] Tomb Raider 6 (#35087) * 5ec366c3-7883-4004-84a2-e4b14bac5b1d/400 * Use lat plan * fix import * fix * 8d0a1b3b-9972-4e53-b9c5-3e13e5e3e404/400 * whitespace * whitespace --- selfdrive/controls/lib/drive_helpers.py | 10 ++++++++++ selfdrive/modeld/modeld.py | 7 ++++--- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 3f99f1bbbb..56eeac3bdf 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -62,3 +62,13 @@ def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0. should_stop = (v_target < vEgoStopping and v_target_1sec < vEgoStopping) return a_target, should_stop + +def curv_from_psis(psi_target, psi_rate, vego, action_t): + vego = np.clip(vego, MIN_SPEED, np.inf) + curv_from_psi = psi_target / (vego * action_t) + return 2*curv_from_psi - psi_rate / vego + +def get_curvature_from_plan(yaws, yaw_rates, t_idxs, vego, action_t): + psi_target = np.interp(action_t, t_idxs, yaws) + psi_rate = yaw_rates[0] + return curv_from_psis(psi_target, psi_rate, vego, action_t) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 9805506432..c1b3dd52d8 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -26,7 +26,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.selfdrive.modeld.constants import ModelConstants, Plan @@ -54,8 +54,9 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log. ModelConstants.T_IDXS, action_t=long_action_t) desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS) - - desired_curvature = model_output['desired_curvature'][0, 0] + desired_curvature = get_curvature_from_plan(plan[:, Plan.T_FROM_CURRENT_EULER][:, 2], + plan[:, Plan.ORIENTATION_RATE][:, 2], + ModelConstants.T_IDXS, v_ego, lat_action_t) if v_ego > MIN_LAT_CONTROL_SPEED: desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) else: diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 2075792f4d..5b9b686829 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b86d130c5db2d772da1b139c136ed86976f37137129a19a6b881fdf641bca198 -size 15578328 +oid sha256:8a478376723ba79e2393ca95e2d3e497571ba6fed113e5f13a36f0e4b4d4a7c5 +size 15588463 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 5e08b78472..0a7b4a803d 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f222d2c528f1763828de01bb55e8979b1e4056e1dbb41350f521d2d2bb09d177 +oid sha256:dad289ae367cefcb862ef1d707fb4919d008f0eeaa1ebaf18df58d8de5a7d96e size 46265585 From 2ceafa2c5714e81de87e2fbba741d529fc7fbd13 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Thu, 1 May 2025 11:44:43 +0200 Subject: [PATCH 090/477] ci: Fix prebuilt label behavior (#864) * Refactor workflow to centralize branch source handling Moved branch resolution logic to a single `SOURCE_BRANCH` variable for consistency and readability. Updated related conditionals and parameters to use this new variable, reducing redundancy and improving maintainability of branch-specific configurations. * Refactor workflow to use repository-defined variables. Replaced hardcoded branch names with repository-defined variables for improved configurability and maintainability. This ensures flexibility by allowing changes via repository settings without modifying the workflow file. --- .../workflows/sunnypilot-build-prebuilt.yaml | 107 ++++++++++-------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/.github/workflows/sunnypilot-build-prebuilt.yaml b/.github/workflows/sunnypilot-build-prebuilt.yaml index c203edfa75..6cd5023a17 100644 --- a/.github/workflows/sunnypilot-build-prebuilt.yaml +++ b/.github/workflows/sunnypilot-build-prebuilt.yaml @@ -8,14 +8,16 @@ env: PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot" # Branch configurations - MASTER_BRANCH: "master" - MASTER_NEW_BRANCH: "master-new" - DEV_C3_SOURCE_BRANCH: "master-dev-c3-new" + STAGING_C3_SOURCE_BRANCH: ${{ vars.STAGING_C3_SOURCE_BRANCH || 'master-new' }} # vars are set on repo settings. + DEV_C3_SOURCE_BRANCH: ${{ vars.DEV_C3_SOURCE_BRANCH || 'master-dev-c3-new' }} # vars are set on repo settings. # Target branch configurations - STAGING_TARGET_BRANCH: "staging-c3-new" - DEV_TARGET_BRANCH: "dev-c3-new" - RELEASE_TARGET_BRANCH: "release-c3-new" + STAGING_TARGET_BRANCH: ${{ vars.STAGING_TARGET_BRANCH || 'staging-c3-new' }} # vars are set on repo settings. + DEV_TARGET_BRANCH: ${{ vars.DEV_TARGET_BRANCH || 'dev-c3-new' }} # vars are set on repo settings. + RELEASE_TARGET_BRANCH: ${{ vars.RELEASE_TARGET_BRANCH || 'release-c3-new' }} # vars are set on repo settings. + + # Runtime configuration + SOURCE_BRANCH: "${{ github.head_ref || github.ref_name }}" on: push: @@ -59,7 +61,7 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive - ref: ${{ github.head_ref || github.ref_name }} + ref: ${{ env.SOURCE_BRANCH }} repository: ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.head.repo.full_name || github.repository }} - run: git lfs pull @@ -67,53 +69,62 @@ jobs: uses: actions/cache@v4 with: path: ${{env.SCONS_CACHE_DIR}} - key: scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-${{ github.sha }} + key: scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }}-${{ github.sha }} # Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.) # for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden. restore-keys: | - scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }} - scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }} - scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }} + scons-${{ runner.os }}-${{ runner.arch }}-${{ env.SOURCE_BRANCH }} + scons-${{ runner.os }}-${{ runner.arch }}-${{ env.STAGING_C3_SOURCE_BRANCH }} scons-${{ runner.os }}-${{ runner.arch }} - - name: Set Configuration - if: (!github.event.pull_request.head.repo.fork) + - name: Set Feature Branch Prebuilt Configuration + id: set_feature_configuration + if: ( + !(env.SOURCE_BRANCH == env.DEV_C3_SOURCE_BRANCH) && + !(env.SOURCE_BRANCH == env.STAGING_C3_SOURCE_BRANCH) && + !(startsWith(github.ref, 'refs/tags/')) + ) run: | - if [[ "${{ github.head_ref || github.ref_name }}" == "${{ env.DEV_C3_SOURCE_BRANCH }}" ]]; then - # Dev configuration - echo "BRANCH_TYPE=dev" >> $GITHUB_ENV - echo "NEW_BRANCH=${{ env.DEV_TARGET_BRANCH }}" >> $GITHUB_ENV - echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV - echo "EXTRA_VERSION_IDENTIFIER=${{ github.run_number }}" >> $GITHUB_ENV - - elif [[ "${{ github.head_ref || github.ref_name }}" == "${{ env.MASTER_BRANCH }}" || "${{ github.ref_name }}" == "${{ env.MASTER_NEW_BRANCH }}" ]]; then - # Master configuration - echo "BRANCH_TYPE=master" >> $GITHUB_ENV - echo "NEW_BRANCH=${{ env.STAGING_TARGET_BRANCH }}" >> $GITHUB_ENV - echo "EXTRA_VERSION_IDENTIFIER=staging" >> $GITHUB_ENV - echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-staging" >> $GITHUB_ENV - - elif [[ "${{ github.ref }}" == refs/tags/* ]]; then - # Tag configuration - echo "BRANCH_TYPE=tag" >> $GITHUB_ENV - echo "NEW_BRANCH=${{ env.RELEASE_TARGET_BRANCH }}" >> $GITHUB_ENV - echo "EXTRA_VERSION_IDENTIFIER=release" >> $GITHUB_ENV - echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-release" >> $GITHUB_ENV - - else - # Feature branch configuration - echo "BRANCH_TYPE=dispatch" >> $GITHUB_ENV - echo "NEW_BRANCH=${{ github.head_ref || github.ref_name }}-prebuilt" >> $GITHUB_ENV - echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV - fi - - - name: Set Configuration (only forks) - if: (github.event.pull_request.head.repo.fork) - run: | - echo "BRANCH_TYPE=dispatch" >> $GITHUB_ENV - echo "NEW_BRANCH=${{ github.head_ref || github.ref_name }}-fork-prebuilt" >> $GITHUB_ENV + echo "NEW_BRANCH=${{ env.SOURCE_BRANCH }}${{ github.event.pull_request.head.repo.fork && '-fork' || '' }}-prebuilt" >> $GITHUB_ENV echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV + - name: Set dev-c3-new prebuilt Configuration + id: set_dev_configuration + if: ( + steps.set_feature_configuration.outcome == 'skipped' && + env.SOURCE_BRANCH == env.DEV_C3_SOURCE_BRANCH + ) + run: | + echo "NEW_BRANCH=${{ env.DEV_TARGET_BRANCH }}" >> $GITHUB_ENV + echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV + echo "EXTRA_VERSION_IDENTIFIER=${{ github.run_number }}" >> $GITHUB_ENV + + - name: Set staging-c3-new prebuilt Configuration + id: set_staging_configuration + if: ( + steps.set_feature_configuration.outcome == 'skipped' && + !contains(github.event_name, 'pull_request') && + steps.set_dev_configuration.outcome == 'skipped' && + (env.SOURCE_BRANCH == env.STAGING_C3_SOURCE_BRANCH) + ) + run: | + echo "NEW_BRANCH=${{ env.STAGING_TARGET_BRANCH }}" >> $GITHUB_ENV + echo "EXTRA_VERSION_IDENTIFIER=staging" >> $GITHUB_ENV + echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-staging" >> $GITHUB_ENV + + - name: Set release-c3-new prebuilt Configuration + id: set_tag_configuration + if: ( + steps.set_feature_configuration.outcome == 'skipped' && + !contains(github.event_name, 'pull_request') && + steps.set_staging_configuration.outcome == 'skipped' && + startsWith(github.ref, 'refs/tags/') + ) + run: | + echo "NEW_BRANCH=${{ env.RELEASE_TARGET_BRANCH }}" >> $GITHUB_ENV + echo "EXTRA_VERSION_IDENTIFIER=release" >> $GITHUB_ENV + echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-release" >> $GITHUB_ENV + - name: Set environment variables id: set-env run: | @@ -278,12 +289,12 @@ jobs: - name: Send Discord Notification env: - DISCORD_WEBHOOK: ${{ contains(fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES), github.head_ref || github.ref_name) && secrets.DISCORD_DEV_FEEDBACK_CHANNEL_WEBHOOK || secrets.DISCORD_DEV_PRIVATE_CHANNEL_WEBHOOK }} + DISCORD_WEBHOOK: ${{ contains(fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES), env.SOURCE_BRANCH) && secrets.DISCORD_DEV_FEEDBACK_CHANNEL_WEBHOOK || secrets.DISCORD_DEV_PRIVATE_CHANNEL_WEBHOOK }} run: | TEMPLATE='${{ vars.DISCORD_GENERAL_UPDATE_NOTICE }}' export EXTRA_VERSION_IDENTIFIER="${{ needs.build.outputs.extra_version_identifier }}" export VERSION="${{ needs.build.outputs.version }}" - export branch_name=${{ github.head_ref || github.ref_name }} + export branch_name=${{ env.SOURCE_BRANCH }} export new_branch=${{ needs.build.outputs.new_branch }} export extra_version_identifier=${{ needs.build.outputs.extra_version_identifier || github.run_number}} echo ${TEMPLATE} | envsubst | jq -c '.' | tee payload.json From 45b273328de61905f5c8f48d21e626ef831ff697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 1 May 2025 11:42:49 -0700 Subject: [PATCH 091/477] Enable online lag learning (#34968) * Use in modeld * Use in torqued * Do not use default value in torqued * Add liveDelay in process replay * update ref --------- Co-authored-by: Bruce Wayne --- selfdrive/locationd/torqued.py | 7 ++++--- selfdrive/modeld/modeld.py | 4 ++-- selfdrive/test/process_replay/process_replay.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index fcaa19df73..5ed86e457f 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -52,7 +52,7 @@ class TorqueBuckets(PointBuckets): class TorqueEstimator(ParameterEstimator): def __init__(self, CP, decimated=False, track_all_points=False): self.hist_len = int(HISTORY / DT_MDL) - self.lag = CP.steerActuatorDelay + .2 # from controlsd + self.lag = 0.0 self.track_all_points = track_all_points # for offline analysis, without max lateral accel or max steer torque filters if decimated: self.min_bucket_points = MIN_BUCKET_POINTS / 10 @@ -175,7 +175,8 @@ class TorqueEstimator(ParameterEstimator): self.raw_points["steer_override"].append(msg.steeringPressed) elif which == "liveCalibration": self.calibrator.feed_live_calib(msg) - + elif which == "liveDelay": + self.lag = msg.lateralDelay # calculate lateral accel from past steering torque elif which == "livePose": if len(self.raw_points['steer_torque']) == self.hist_len: @@ -241,7 +242,7 @@ def main(demo=False): config_realtime_process([0, 1, 2, 3], 5) pm = messaging.PubMaster(['liveTorqueParameters']) - sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose'], poll='livePose') + sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose') params = Params() estimator = TorqueEstimator(messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index c1b3dd52d8..dde742f159 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -221,7 +221,7 @@ def main(demo=False): # messaging pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) - sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"]) + sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) publish_state = PublishState() params = Params() @@ -248,7 +248,6 @@ def main(demo=False): # TODO this needs more thought, use .2s extra for now to estimate other delays # TODO Move smooth seconds to action function - lat_delay = CP.steerActuatorDelay + .2 + LAT_SMOOTH_SECONDS long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -292,6 +291,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) + lat_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 6139846322..3183db0c3a 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -568,7 +568,7 @@ CONFIGS = [ ), ProcessConfig( proc_name="torqued", - pubs=["livePose", "liveCalibration", "carState", "carControl", "carOutput"], + pubs=["livePose", "liveCalibration", "liveDelay", "carState", "carControl", "carOutput"], subs=["liveTorqueParameters"], ignore=["logMonoTime"], init_callback=get_car_params_callback, @@ -577,7 +577,7 @@ CONFIGS = [ ), ProcessConfig( proc_name="modeld", - pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "driverMonitoringState", "carState", "carControl"], + pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "liveDelay", "driverMonitoringState", "carState", "carControl"], subs=["modelV2", "drivingModelData", "cameraOdometry"], ignore=["logMonoTime", "modelV2.frameDropPerc", "modelV2.modelExecutionTime", "drivingModelData.frameDropPerc", "drivingModelData.modelExecutionTime"], should_recv_callback=ModeldCameraSyncRcvCallback(), diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 5f8610d58f..3dd3eab25f 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -4c7ac1a362933af9d97697979162087c1ab2584b \ No newline at end of file +7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 \ No newline at end of file From afcd031cf1daca1e4ff8b49b0004f44bdda49699 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Thu, 1 May 2025 13:59:07 -0700 Subject: [PATCH 092/477] [bot] Update Python packages (#35095) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- uv.lock | 3262 +++++++++++++++++++++++++------------------------- 2 files changed, 1632 insertions(+), 1632 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index b35c84f07c..0d3b96f1db 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit b35c84f07cfcd7ef02246f8eba69bbfd6216431c +Subproject commit 0d3b96f1dbb6d6318b123e6c481248685c805f18 diff --git a/uv.lock b/uv.lock index 4981010457..eb43ede212 100644 --- a/uv.lock +++ b/uv.lock @@ -14,9 +14,9 @@ resolution-markers = [ name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload_time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload_time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] @@ -32,40 +32,40 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/e7/fa1a8c00e2c54b05dc8cb5d1439f627f7c267874e3f7bb047146116020f9/aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a", size = 7678653, upload_time = "2025-04-21T09:43:09.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/e7/fa1a8c00e2c54b05dc8cb5d1439f627f7c267874e3f7bb047146116020f9/aiohttp-3.11.18.tar.gz", hash = "sha256:ae856e1138612b7e412db63b7708735cff4d38d0399f6a5435d3dac2669f558a", size = 7678653, upload-time = "2025-04-21T09:43:09.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/10/fd9ee4f9e042818c3c2390054c08ccd34556a3cb209d83285616434cf93e/aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9", size = 712088, upload_time = "2025-04-21T09:40:55.776Z" }, - { url = "https://files.pythonhosted.org/packages/22/eb/6a77f055ca56f7aae2cd2a5607a3c9e7b9554f1497a069dcfcb52bfc9540/aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b", size = 471450, upload_time = "2025-04-21T09:40:57.301Z" }, - { url = "https://files.pythonhosted.org/packages/78/dc/5f3c0d27c91abf0bb5d103e9c9b0ff059f60cf6031a5f06f456c90731f42/aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66", size = 457836, upload_time = "2025-04-21T09:40:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/49/7b/55b65af9ef48b9b811c91ff8b5b9de9650c71147f10523e278d297750bc8/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756", size = 1690978, upload_time = "2025-04-21T09:41:00.795Z" }, - { url = "https://files.pythonhosted.org/packages/a2/5a/3f8938c4f68ae400152b42742653477fc625d6bfe02e764f3521321c8442/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717", size = 1745307, upload_time = "2025-04-21T09:41:02.89Z" }, - { url = "https://files.pythonhosted.org/packages/b4/42/89b694a293333ef6f771c62da022163bcf44fb03d4824372d88e3dc12530/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4", size = 1780692, upload_time = "2025-04-21T09:41:04.461Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ce/1a75384e01dd1bf546898b6062b1b5f7a59b6692ef802e4dd6db64fed264/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f", size = 1676934, upload_time = "2025-04-21T09:41:06.728Z" }, - { url = "https://files.pythonhosted.org/packages/a5/31/442483276e6c368ab5169797d9873b5875213cbcf7e74b95ad1c5003098a/aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361", size = 1621190, upload_time = "2025-04-21T09:41:08.293Z" }, - { url = "https://files.pythonhosted.org/packages/7b/83/90274bf12c079457966008a58831a99675265b6a34b505243e004b408934/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1", size = 1658947, upload_time = "2025-04-21T09:41:11.054Z" }, - { url = "https://files.pythonhosted.org/packages/91/c1/da9cee47a0350b78fdc93670ebe7ad74103011d7778ab4c382ca4883098d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421", size = 1654443, upload_time = "2025-04-21T09:41:13.213Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f2/73cbe18dc25d624f79a09448adfc4972f82ed6088759ddcf783cd201956c/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e", size = 1644169, upload_time = "2025-04-21T09:41:14.827Z" }, - { url = "https://files.pythonhosted.org/packages/5b/32/970b0a196c4dccb1b0cfa5b4dc3b20f63d76f1c608f41001a84b2fd23c3d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d", size = 1728532, upload_time = "2025-04-21T09:41:17.168Z" }, - { url = "https://files.pythonhosted.org/packages/0b/50/b1dc810a41918d2ea9574e74125eb053063bc5e14aba2d98966f7d734da0/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f", size = 1750310, upload_time = "2025-04-21T09:41:19.353Z" }, - { url = "https://files.pythonhosted.org/packages/95/24/39271f5990b35ff32179cc95537e92499d3791ae82af7dcf562be785cd15/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd", size = 1691580, upload_time = "2025-04-21T09:41:21.868Z" }, - { url = "https://files.pythonhosted.org/packages/6b/78/75d0353feb77f041460564f12fe58e456436bbc00cbbf5d676dbf0038cc2/aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d", size = 417565, upload_time = "2025-04-21T09:41:24.78Z" }, - { url = "https://files.pythonhosted.org/packages/ed/97/b912dcb654634a813f8518de359364dfc45976f822116e725dc80a688eee/aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6", size = 443652, upload_time = "2025-04-21T09:41:26.48Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d2/5bc436f42bf4745c55f33e1e6a2d69e77075d3e768e3d1a34f96ee5298aa/aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2", size = 706671, upload_time = "2025-04-21T09:41:28.021Z" }, - { url = "https://files.pythonhosted.org/packages/fe/d0/2dbabecc4e078c0474abb40536bbde717fb2e39962f41c5fc7a216b18ea7/aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508", size = 466169, upload_time = "2025-04-21T09:41:29.783Z" }, - { url = "https://files.pythonhosted.org/packages/70/84/19edcf0b22933932faa6e0be0d933a27bd173da02dc125b7354dff4d8da4/aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e", size = 457554, upload_time = "2025-04-21T09:41:31.327Z" }, - { url = "https://files.pythonhosted.org/packages/32/d0/e8d1f034ae5624a0f21e4fb3feff79342ce631f3a4d26bd3e58b31ef033b/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f", size = 1690154, upload_time = "2025-04-21T09:41:33.541Z" }, - { url = "https://files.pythonhosted.org/packages/16/de/2f9dbe2ac6f38f8495562077131888e0d2897e3798a0ff3adda766b04a34/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f", size = 1733402, upload_time = "2025-04-21T09:41:35.634Z" }, - { url = "https://files.pythonhosted.org/packages/e0/04/bd2870e1e9aef990d14b6df2a695f17807baf5c85a4c187a492bda569571/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec", size = 1783958, upload_time = "2025-04-21T09:41:37.456Z" }, - { url = "https://files.pythonhosted.org/packages/23/06/4203ffa2beb5bedb07f0da0f79b7d9039d1c33f522e0d1a2d5b6218e6f2e/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6", size = 1695288, upload_time = "2025-04-21T09:41:39.756Z" }, - { url = "https://files.pythonhosted.org/packages/30/b2/e2285dda065d9f29ab4b23d8bcc81eb881db512afb38a3f5247b191be36c/aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009", size = 1618871, upload_time = "2025-04-21T09:41:41.972Z" }, - { url = "https://files.pythonhosted.org/packages/57/e0/88f2987885d4b646de2036f7296ebea9268fdbf27476da551c1a7c158bc0/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4", size = 1646262, upload_time = "2025-04-21T09:41:44.192Z" }, - { url = "https://files.pythonhosted.org/packages/e0/19/4d2da508b4c587e7472a032290b2981f7caeca82b4354e19ab3df2f51d56/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9", size = 1677431, upload_time = "2025-04-21T09:41:46.049Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ae/047473ea50150a41440f3265f53db1738870b5a1e5406ece561ca61a3bf4/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb", size = 1637430, upload_time = "2025-04-21T09:41:47.973Z" }, - { url = "https://files.pythonhosted.org/packages/11/32/c6d1e3748077ce7ee13745fae33e5cb1dac3e3b8f8787bf738a93c94a7d2/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda", size = 1703342, upload_time = "2025-04-21T09:41:50.323Z" }, - { url = "https://files.pythonhosted.org/packages/c5/1d/a3b57bfdbe285f0d45572d6d8f534fd58761da3e9cbc3098372565005606/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1", size = 1740600, upload_time = "2025-04-21T09:41:52.111Z" }, - { url = "https://files.pythonhosted.org/packages/a5/71/f9cd2fed33fa2b7ce4d412fb7876547abb821d5b5520787d159d0748321d/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea", size = 1695131, upload_time = "2025-04-21T09:41:53.94Z" }, - { url = "https://files.pythonhosted.org/packages/97/97/d1248cd6d02b9de6aa514793d0dcb20099f0ec47ae71a933290116c070c5/aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8", size = 412442, upload_time = "2025-04-21T09:41:55.689Z" }, - { url = "https://files.pythonhosted.org/packages/33/9a/e34e65506e06427b111e19218a99abf627638a9703f4b8bcc3e3021277ed/aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8", size = 439444, upload_time = "2025-04-21T09:41:57.977Z" }, + { url = "https://files.pythonhosted.org/packages/2f/10/fd9ee4f9e042818c3c2390054c08ccd34556a3cb209d83285616434cf93e/aiohttp-3.11.18-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:427fdc56ccb6901ff8088544bde47084845ea81591deb16f957897f0f0ba1be9", size = 712088, upload-time = "2025-04-21T09:40:55.776Z" }, + { url = "https://files.pythonhosted.org/packages/22/eb/6a77f055ca56f7aae2cd2a5607a3c9e7b9554f1497a069dcfcb52bfc9540/aiohttp-3.11.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c828b6d23b984255b85b9b04a5b963a74278b7356a7de84fda5e3b76866597b", size = 471450, upload-time = "2025-04-21T09:40:57.301Z" }, + { url = "https://files.pythonhosted.org/packages/78/dc/5f3c0d27c91abf0bb5d103e9c9b0ff059f60cf6031a5f06f456c90731f42/aiohttp-3.11.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c2eaa145bb36b33af1ff2860820ba0589e165be4ab63a49aebfd0981c173b66", size = 457836, upload-time = "2025-04-21T09:40:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/49/7b/55b65af9ef48b9b811c91ff8b5b9de9650c71147f10523e278d297750bc8/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d518ce32179f7e2096bf4e3e8438cf445f05fedd597f252de9f54c728574756", size = 1690978, upload-time = "2025-04-21T09:41:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5a/3f8938c4f68ae400152b42742653477fc625d6bfe02e764f3521321c8442/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700055a6e05c2f4711011a44364020d7a10fbbcd02fbf3e30e8f7e7fddc8717", size = 1745307, upload-time = "2025-04-21T09:41:02.89Z" }, + { url = "https://files.pythonhosted.org/packages/b4/42/89b694a293333ef6f771c62da022163bcf44fb03d4824372d88e3dc12530/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bd1cde83e4684324e6ee19adfc25fd649d04078179890be7b29f76b501de8e4", size = 1780692, upload-time = "2025-04-21T09:41:04.461Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ce/1a75384e01dd1bf546898b6062b1b5f7a59b6692ef802e4dd6db64fed264/aiohttp-3.11.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b8870fe1c9a201b8c0d12c94fe781b918664766728783241a79e0468427e4f", size = 1676934, upload-time = "2025-04-21T09:41:06.728Z" }, + { url = "https://files.pythonhosted.org/packages/a5/31/442483276e6c368ab5169797d9873b5875213cbcf7e74b95ad1c5003098a/aiohttp-3.11.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25557982dd36b9e32c0a3357f30804e80790ec2c4d20ac6bcc598533e04c6361", size = 1621190, upload-time = "2025-04-21T09:41:08.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/83/90274bf12c079457966008a58831a99675265b6a34b505243e004b408934/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e889c9df381a2433802991288a61e5a19ceb4f61bd14f5c9fa165655dcb1fd1", size = 1658947, upload-time = "2025-04-21T09:41:11.054Z" }, + { url = "https://files.pythonhosted.org/packages/91/c1/da9cee47a0350b78fdc93670ebe7ad74103011d7778ab4c382ca4883098d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9ea345fda05bae217b6cce2acf3682ce3b13d0d16dd47d0de7080e5e21362421", size = 1654443, upload-time = "2025-04-21T09:41:13.213Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f2/73cbe18dc25d624f79a09448adfc4972f82ed6088759ddcf783cd201956c/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9f26545b9940c4b46f0a9388fd04ee3ad7064c4017b5a334dd450f616396590e", size = 1644169, upload-time = "2025-04-21T09:41:14.827Z" }, + { url = "https://files.pythonhosted.org/packages/5b/32/970b0a196c4dccb1b0cfa5b4dc3b20f63d76f1c608f41001a84b2fd23c3d/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3a621d85e85dccabd700294494d7179ed1590b6d07a35709bb9bd608c7f5dd1d", size = 1728532, upload-time = "2025-04-21T09:41:17.168Z" }, + { url = "https://files.pythonhosted.org/packages/0b/50/b1dc810a41918d2ea9574e74125eb053063bc5e14aba2d98966f7d734da0/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9c23fd8d08eb9c2af3faeedc8c56e134acdaf36e2117ee059d7defa655130e5f", size = 1750310, upload-time = "2025-04-21T09:41:19.353Z" }, + { url = "https://files.pythonhosted.org/packages/95/24/39271f5990b35ff32179cc95537e92499d3791ae82af7dcf562be785cd15/aiohttp-3.11.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9e6b0e519067caa4fd7fb72e3e8002d16a68e84e62e7291092a5433763dc0dd", size = 1691580, upload-time = "2025-04-21T09:41:21.868Z" }, + { url = "https://files.pythonhosted.org/packages/6b/78/75d0353feb77f041460564f12fe58e456436bbc00cbbf5d676dbf0038cc2/aiohttp-3.11.18-cp311-cp311-win32.whl", hash = "sha256:122f3e739f6607e5e4c6a2f8562a6f476192a682a52bda8b4c6d4254e1138f4d", size = 417565, upload-time = "2025-04-21T09:41:24.78Z" }, + { url = "https://files.pythonhosted.org/packages/ed/97/b912dcb654634a813f8518de359364dfc45976f822116e725dc80a688eee/aiohttp-3.11.18-cp311-cp311-win_amd64.whl", hash = "sha256:e6f3c0a3a1e73e88af384b2e8a0b9f4fb73245afd47589df2afcab6b638fa0e6", size = 443652, upload-time = "2025-04-21T09:41:26.48Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d2/5bc436f42bf4745c55f33e1e6a2d69e77075d3e768e3d1a34f96ee5298aa/aiohttp-3.11.18-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63d71eceb9cad35d47d71f78edac41fcd01ff10cacaa64e473d1aec13fa02df2", size = 706671, upload-time = "2025-04-21T09:41:28.021Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d0/2dbabecc4e078c0474abb40536bbde717fb2e39962f41c5fc7a216b18ea7/aiohttp-3.11.18-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1929da615840969929e8878d7951b31afe0bac883d84418f92e5755d7b49508", size = 466169, upload-time = "2025-04-21T09:41:29.783Z" }, + { url = "https://files.pythonhosted.org/packages/70/84/19edcf0b22933932faa6e0be0d933a27bd173da02dc125b7354dff4d8da4/aiohttp-3.11.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d0aebeb2392f19b184e3fdd9e651b0e39cd0f195cdb93328bd124a1d455cd0e", size = 457554, upload-time = "2025-04-21T09:41:31.327Z" }, + { url = "https://files.pythonhosted.org/packages/32/d0/e8d1f034ae5624a0f21e4fb3feff79342ce631f3a4d26bd3e58b31ef033b/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3849ead845e8444f7331c284132ab314b4dac43bfae1e3cf350906d4fff4620f", size = 1690154, upload-time = "2025-04-21T09:41:33.541Z" }, + { url = "https://files.pythonhosted.org/packages/16/de/2f9dbe2ac6f38f8495562077131888e0d2897e3798a0ff3adda766b04a34/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e8452ad6b2863709f8b3d615955aa0807bc093c34b8e25b3b52097fe421cb7f", size = 1733402, upload-time = "2025-04-21T09:41:35.634Z" }, + { url = "https://files.pythonhosted.org/packages/e0/04/bd2870e1e9aef990d14b6df2a695f17807baf5c85a4c187a492bda569571/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b8d2b42073611c860a37f718b3d61ae8b4c2b124b2e776e2c10619d920350ec", size = 1783958, upload-time = "2025-04-21T09:41:37.456Z" }, + { url = "https://files.pythonhosted.org/packages/23/06/4203ffa2beb5bedb07f0da0f79b7d9039d1c33f522e0d1a2d5b6218e6f2e/aiohttp-3.11.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fbf91f6a0ac317c0a07eb328a1384941872f6761f2e6f7208b63c4cc0a7ff6", size = 1695288, upload-time = "2025-04-21T09:41:39.756Z" }, + { url = "https://files.pythonhosted.org/packages/30/b2/e2285dda065d9f29ab4b23d8bcc81eb881db512afb38a3f5247b191be36c/aiohttp-3.11.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ff5625413fec55216da5eaa011cf6b0a2ed67a565914a212a51aa3755b0009", size = 1618871, upload-time = "2025-04-21T09:41:41.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/e0/88f2987885d4b646de2036f7296ebea9268fdbf27476da551c1a7c158bc0/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f33a92a2fde08e8c6b0c61815521324fc1612f397abf96eed86b8e31618fdb4", size = 1646262, upload-time = "2025-04-21T09:41:44.192Z" }, + { url = "https://files.pythonhosted.org/packages/e0/19/4d2da508b4c587e7472a032290b2981f7caeca82b4354e19ab3df2f51d56/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:11d5391946605f445ddafda5eab11caf310f90cdda1fd99865564e3164f5cff9", size = 1677431, upload-time = "2025-04-21T09:41:46.049Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/047473ea50150a41440f3265f53db1738870b5a1e5406ece561ca61a3bf4/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3cc314245deb311364884e44242e00c18b5896e4fe6d5f942e7ad7e4cb640adb", size = 1637430, upload-time = "2025-04-21T09:41:47.973Z" }, + { url = "https://files.pythonhosted.org/packages/11/32/c6d1e3748077ce7ee13745fae33e5cb1dac3e3b8f8787bf738a93c94a7d2/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f421843b0f70740772228b9e8093289924359d306530bcd3926f39acbe1adda", size = 1703342, upload-time = "2025-04-21T09:41:50.323Z" }, + { url = "https://files.pythonhosted.org/packages/c5/1d/a3b57bfdbe285f0d45572d6d8f534fd58761da3e9cbc3098372565005606/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e220e7562467dc8d589e31c1acd13438d82c03d7f385c9cd41a3f6d1d15807c1", size = 1740600, upload-time = "2025-04-21T09:41:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a5/71/f9cd2fed33fa2b7ce4d412fb7876547abb821d5b5520787d159d0748321d/aiohttp-3.11.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ab2ef72f8605046115bc9aa8e9d14fd49086d405855f40b79ed9e5c1f9f4faea", size = 1695131, upload-time = "2025-04-21T09:41:53.94Z" }, + { url = "https://files.pythonhosted.org/packages/97/97/d1248cd6d02b9de6aa514793d0dcb20099f0ec47ae71a933290116c070c5/aiohttp-3.11.18-cp312-cp312-win32.whl", hash = "sha256:12a62691eb5aac58d65200c7ae94d73e8a65c331c3a86a2e9670927e94339ee8", size = 412442, upload-time = "2025-04-21T09:41:55.689Z" }, + { url = "https://files.pythonhosted.org/packages/33/9a/e34e65506e06427b111e19218a99abf627638a9703f4b8bcc3e3021277ed/aiohttp-3.11.18-cp312-cp312-win_amd64.whl", hash = "sha256:364329f319c499128fd5cd2d1c31c44f234c58f9b96cc57f743d16ec4f3238c8", size = 439444, upload-time = "2025-04-21T09:41:57.977Z" }, ] [[package]] @@ -76,9 +76,9 @@ dependencies = [ { name = "dnspython" }, { name = "ifaddr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/a2/45dfab1d5a7f96c48595a5770379acf406cdf02a2cd1ac1729b599322b08/aioice-0.10.1.tar.gz", hash = "sha256:5c8e1422103448d171925c678fb39795e5fe13d79108bebb00aa75a899c2094a", size = 44304, upload_time = "2025-04-13T08:15:25.629Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/a2/45dfab1d5a7f96c48595a5770379acf406cdf02a2cd1ac1729b599322b08/aioice-0.10.1.tar.gz", hash = "sha256:5c8e1422103448d171925c678fb39795e5fe13d79108bebb00aa75a899c2094a", size = 44304, upload-time = "2025-04-13T08:15:25.629Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/58/af07dda649c22a1ae954ffb7aaaf4d4a57f1bf00ebdf62307affc0b8552f/aioice-0.10.1-py3-none-any.whl", hash = "sha256:f31ae2abc8608b1283ed5f21aebd7b6bd472b152ff9551e9b559b2d8efed79e9", size = 24872, upload_time = "2025-04-13T08:15:24.044Z" }, + { url = "https://files.pythonhosted.org/packages/3b/58/af07dda649c22a1ae954ffb7aaaf4d4a57f1bf00ebdf62307affc0b8552f/aioice-0.10.1-py3-none-any.whl", hash = "sha256:f31ae2abc8608b1283ed5f21aebd7b6bd472b152ff9551e9b559b2d8efed79e9", size = 24872, upload-time = "2025-04-13T08:15:24.044Z" }, ] [[package]] @@ -95,15 +95,15 @@ dependencies = [ { name = "pylibsrtp" }, { name = "pyopenssl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/f8/408e092748521889c9d33dddcef920afd9891cf6db4615ba6b6bfe114ff8/aiortc-1.10.1.tar.gz", hash = "sha256:64926ad86bde20c1a4dacb7c3a164e57b522606b70febe261fada4acf79641b5", size = 1179406, upload_time = "2025-02-02T17:36:38.684Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/f8/408e092748521889c9d33dddcef920afd9891cf6db4615ba6b6bfe114ff8/aiortc-1.10.1.tar.gz", hash = "sha256:64926ad86bde20c1a4dacb7c3a164e57b522606b70febe261fada4acf79641b5", size = 1179406, upload-time = "2025-02-02T17:36:38.684Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/6b/74547a30d1ddcc81f905ef4ff7fcc2c89b7482cb2045688f2aaa4fa918aa/aiortc-1.10.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3bef536f38394b518aefae9dbf9cdd08f39e4c425f316f9692f0d8dc724810bd", size = 1218457, upload_time = "2025-02-02T17:36:23.172Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/b4ccf39cd18e366ace2a11dc7d98ed55967b4b325707386b5788149db15e/aiortc-1.10.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8842c02e38513d9432ef22982572833487bb015f23348fa10a690616dbf55143", size = 898855, upload_time = "2025-02-02T17:36:25.9Z" }, - { url = "https://files.pythonhosted.org/packages/a4/e9/2676de48b493787d8b03129713e6bb2dfbacca2a565090f2a89cbad71f96/aiortc-1.10.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:954a420de01c0bf6b07a0c58b662029b1c4204ddbd8f5c4162bbdebd43f882b1", size = 1750403, upload_time = "2025-02-02T17:36:28.446Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9d/ab6d09183cdaf5df060923d9bd5c9ed5fb1802661d9401dba35f3c85a57b/aiortc-1.10.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7c0d46fb30307a9d7deb4b7d66f0b0e73b77a7221b063fb6dc78821a5d2aa1e", size = 1867886, upload_time = "2025-02-02T17:36:30.209Z" }, - { url = "https://files.pythonhosted.org/packages/c2/71/0b5666e6b965dbd9a7f331aa827a6c3ab3eb4d582fefb686a7f4227b7954/aiortc-1.10.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89582f6923046f79f15d9045f432bc78191eacc95f6bed18714e86ec935188d9", size = 1893709, upload_time = "2025-02-02T17:36:32.342Z" }, - { url = "https://files.pythonhosted.org/packages/9d/0a/8c0c78fad79ef595a0ed6e2ab413900e6bd0eac65fc5c31c9d8736bff909/aiortc-1.10.1-cp39-abi3-win32.whl", hash = "sha256:d1cbe87f740b33ffaa8e905f21092773e74916be338b64b81c8b79af4c3847eb", size = 923265, upload_time = "2025-02-02T17:36:34.685Z" }, - { url = "https://files.pythonhosted.org/packages/73/12/a27dd588a4988021da88cb4d338d8ee65ac097afc14e9193ab0be4a48790/aiortc-1.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:c9a5a0b23f8a77540068faec8837fa0a65b0396c20f09116bdb874b75e0b6abe", size = 1009488, upload_time = "2025-02-02T17:36:36.317Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6b/74547a30d1ddcc81f905ef4ff7fcc2c89b7482cb2045688f2aaa4fa918aa/aiortc-1.10.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3bef536f38394b518aefae9dbf9cdd08f39e4c425f316f9692f0d8dc724810bd", size = 1218457, upload-time = "2025-02-02T17:36:23.172Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/b4ccf39cd18e366ace2a11dc7d98ed55967b4b325707386b5788149db15e/aiortc-1.10.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8842c02e38513d9432ef22982572833487bb015f23348fa10a690616dbf55143", size = 898855, upload-time = "2025-02-02T17:36:25.9Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/2676de48b493787d8b03129713e6bb2dfbacca2a565090f2a89cbad71f96/aiortc-1.10.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:954a420de01c0bf6b07a0c58b662029b1c4204ddbd8f5c4162bbdebd43f882b1", size = 1750403, upload-time = "2025-02-02T17:36:28.446Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9d/ab6d09183cdaf5df060923d9bd5c9ed5fb1802661d9401dba35f3c85a57b/aiortc-1.10.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7c0d46fb30307a9d7deb4b7d66f0b0e73b77a7221b063fb6dc78821a5d2aa1e", size = 1867886, upload-time = "2025-02-02T17:36:30.209Z" }, + { url = "https://files.pythonhosted.org/packages/c2/71/0b5666e6b965dbd9a7f331aa827a6c3ab3eb4d582fefb686a7f4227b7954/aiortc-1.10.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89582f6923046f79f15d9045f432bc78191eacc95f6bed18714e86ec935188d9", size = 1893709, upload-time = "2025-02-02T17:36:32.342Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0a/8c0c78fad79ef595a0ed6e2ab413900e6bd0eac65fc5c31c9d8736bff909/aiortc-1.10.1-cp39-abi3-win32.whl", hash = "sha256:d1cbe87f740b33ffaa8e905f21092773e74916be338b64b81c8b79af4c3847eb", size = 923265, upload-time = "2025-02-02T17:36:34.685Z" }, + { url = "https://files.pythonhosted.org/packages/73/12/a27dd588a4988021da88cb4d338d8ee65ac097afc14e9193ab0be4a48790/aiortc-1.10.1-cp39-abi3-win_amd64.whl", hash = "sha256:c9a5a0b23f8a77540068faec8837fa0a65b0396c20f09116bdb874b75e0b6abe", size = 1009488, upload-time = "2025-02-02T17:36:36.317Z" }, ] [[package]] @@ -113,38 +113,38 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424, upload_time = "2024-12-13T17:10:40.86Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424, upload-time = "2024-12-13T17:10:40.86Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload_time = "2024-12-13T17:10:38.469Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597, upload-time = "2024-12-13T17:10:38.469Z" }, ] [[package]] name = "attrs" version = "25.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload_time = "2025-03-13T11:10:22.779Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload_time = "2025-03-13T11:10:21.14Z" }, + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, ] [[package]] name = "av" version = "13.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/9d/486d31e76784cc0ad943f420c5e05867263b32b37e2f4b0f7f22fdc1ca3a/av-13.1.0.tar.gz", hash = "sha256:d3da736c55847d8596eb8c26c60e036f193001db3bc5c10da8665622d906c17e", size = 3957908, upload_time = "2024-10-06T04:54:57.507Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/9d/486d31e76784cc0ad943f420c5e05867263b32b37e2f4b0f7f22fdc1ca3a/av-13.1.0.tar.gz", hash = "sha256:d3da736c55847d8596eb8c26c60e036f193001db3bc5c10da8665622d906c17e", size = 3957908, upload-time = "2024-10-06T04:54:57.507Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/54/c4227080c9700384db90072ace70d89b6a288b3748bd2ec0e32580a49e7f/av-13.1.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:867385e6701464a5c95903e24d2e0df1c7e0dbf211ed91d0ce639cd687373e10", size = 24255112, upload_time = "2024-10-06T04:52:48.49Z" }, - { url = "https://files.pythonhosted.org/packages/32/4a/eb9348231655ca99b200b380f4edbceff7358c927a285badcc84b18fb1c9/av-13.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb7a3f319401a46b0017771268ff4928501e77cf00b1a2aa0721e20b2fd1146e", size = 19467930, upload_time = "2024-10-06T04:52:52.118Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/48c80252bdbc3a75a54dd205a7fab8f613914009b9e5416202757208e040/av-13.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad904f860147bceaca65b0d3174a8153f35c570d465161d210f1879970b15559", size = 32207671, upload_time = "2024-10-06T04:52:55.82Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/3332c7fa8c43b65680a94f279ea3e832b5500de3a1392bac6112881e984b/av-13.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a906e017b29d0eb80d9ccf7a98d19268122da792dbb68eb741cfebba156e6aed", size = 31520911, upload_time = "2024-10-06T04:52:59.231Z" }, - { url = "https://files.pythonhosted.org/packages/e5/bb/2e03acb9b27591d97f700a3a6c27cfd1bc53fa148177747eda8a70cca1e9/av-13.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ce894d7847897da7be63277a0875bd93c51327134ac226c67978de014c7979f", size = 34048399, upload_time = "2024-10-06T04:53:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/85/44/527aa3b65947d42cfe829326026edf0cd1a8c459390076034be275616c36/av-13.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:384bcdb5fc3238a263a5a25cc9efc690859fa4148cc4b07e00fae927178db22a", size = 25779569, upload_time = "2024-10-06T04:53:07.582Z" }, - { url = "https://files.pythonhosted.org/packages/9b/aa/4bdd8ce59173574fc6e0c282c71ee6f96fca82643d97bf172bc4cb5a5674/av-13.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:261dbc3f4b55f4f8f3375b10b2258fca7f2ab7a6365c01bc65e77a0d5327a195", size = 24268674, upload_time = "2024-10-06T04:53:11.251Z" }, - { url = "https://files.pythonhosted.org/packages/17/b4/b267dd5bad99eed49ec6731827c6bcb5ab03864bf732a7ebb81e3df79911/av-13.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83d259ef86b9054eb914bc7c6a7f6092a6d75cb939295e70ee979cfd92a67b99", size = 19475617, upload_time = "2024-10-06T04:53:13.832Z" }, - { url = "https://files.pythonhosted.org/packages/68/32/4209e51f54d7b54a1feb576d309c671ed1ff437b54fcc4ec68c239199e0a/av-13.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b4d3ca159eceab97e3c0fb08fe756520fb95508417f76e48198fda2a5b0806", size = 32468873, upload_time = "2024-10-06T04:53:17.639Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d8/c174da5f06b24f3c9e36f91fd02a7411c39da9ce792c17964260d4be675e/av-13.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40e8f757e373b73a2dc4640852a00cce4a4a92ef19b2e642a96d6994cd1fffbf", size = 31818484, upload_time = "2024-10-06T04:53:21.509Z" }, - { url = "https://files.pythonhosted.org/packages/7f/22/0dd8d1d5cad415772bb707d16aea8b81cf75d340d11d3668eea43468c730/av-13.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8aaec2c0bfd024359db3821d679009d4e637e1bee0321d20f61c54ed6b20f41", size = 34398652, upload_time = "2024-10-06T04:53:25.798Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ff/48fa68888b8d5bae36d915556ff18f9e5fdc6b5ff5ae23dc4904c9713168/av-13.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ea0deab0e6a739cb742fba2a3983d8102f7516a3cdf3c46669f3cac0ed1f351", size = 25781343, upload_time = "2024-10-06T04:53:29.577Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/c4227080c9700384db90072ace70d89b6a288b3748bd2ec0e32580a49e7f/av-13.1.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:867385e6701464a5c95903e24d2e0df1c7e0dbf211ed91d0ce639cd687373e10", size = 24255112, upload-time = "2024-10-06T04:52:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/32/4a/eb9348231655ca99b200b380f4edbceff7358c927a285badcc84b18fb1c9/av-13.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb7a3f319401a46b0017771268ff4928501e77cf00b1a2aa0721e20b2fd1146e", size = 19467930, upload-time = "2024-10-06T04:52:52.118Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/48c80252bdbc3a75a54dd205a7fab8f613914009b9e5416202757208e040/av-13.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad904f860147bceaca65b0d3174a8153f35c570d465161d210f1879970b15559", size = 32207671, upload-time = "2024-10-06T04:52:55.82Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/3332c7fa8c43b65680a94f279ea3e832b5500de3a1392bac6112881e984b/av-13.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a906e017b29d0eb80d9ccf7a98d19268122da792dbb68eb741cfebba156e6aed", size = 31520911, upload-time = "2024-10-06T04:52:59.231Z" }, + { url = "https://files.pythonhosted.org/packages/e5/bb/2e03acb9b27591d97f700a3a6c27cfd1bc53fa148177747eda8a70cca1e9/av-13.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ce894d7847897da7be63277a0875bd93c51327134ac226c67978de014c7979f", size = 34048399, upload-time = "2024-10-06T04:53:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/85/44/527aa3b65947d42cfe829326026edf0cd1a8c459390076034be275616c36/av-13.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:384bcdb5fc3238a263a5a25cc9efc690859fa4148cc4b07e00fae927178db22a", size = 25779569, upload-time = "2024-10-06T04:53:07.582Z" }, + { url = "https://files.pythonhosted.org/packages/9b/aa/4bdd8ce59173574fc6e0c282c71ee6f96fca82643d97bf172bc4cb5a5674/av-13.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:261dbc3f4b55f4f8f3375b10b2258fca7f2ab7a6365c01bc65e77a0d5327a195", size = 24268674, upload-time = "2024-10-06T04:53:11.251Z" }, + { url = "https://files.pythonhosted.org/packages/17/b4/b267dd5bad99eed49ec6731827c6bcb5ab03864bf732a7ebb81e3df79911/av-13.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83d259ef86b9054eb914bc7c6a7f6092a6d75cb939295e70ee979cfd92a67b99", size = 19475617, upload-time = "2024-10-06T04:53:13.832Z" }, + { url = "https://files.pythonhosted.org/packages/68/32/4209e51f54d7b54a1feb576d309c671ed1ff437b54fcc4ec68c239199e0a/av-13.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b4d3ca159eceab97e3c0fb08fe756520fb95508417f76e48198fda2a5b0806", size = 32468873, upload-time = "2024-10-06T04:53:17.639Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d8/c174da5f06b24f3c9e36f91fd02a7411c39da9ce792c17964260d4be675e/av-13.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40e8f757e373b73a2dc4640852a00cce4a4a92ef19b2e642a96d6994cd1fffbf", size = 31818484, upload-time = "2024-10-06T04:53:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/7f/22/0dd8d1d5cad415772bb707d16aea8b81cf75d340d11d3668eea43468c730/av-13.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8aaec2c0bfd024359db3821d679009d4e637e1bee0321d20f61c54ed6b20f41", size = 34398652, upload-time = "2024-10-06T04:53:25.798Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/48fa68888b8d5bae36d915556ff18f9e5fdc6b5ff5ae23dc4904c9713168/av-13.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:5ea0deab0e6a739cb742fba2a3983d8102f7516a3cdf3c46669f3cac0ed1f351", size = 25781343, upload-time = "2024-10-06T04:53:29.577Z" }, ] [[package]] @@ -156,9 +156,9 @@ dependencies = [ { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/aa/7c9db8edd626f1a7d99d09ef7926f6f4fb34d5f9fa00dc394afdfe8e2a80/azure_core-1.33.0.tar.gz", hash = "sha256:f367aa07b5e3005fec2c1e184b882b0b039910733907d001c20fb08ebb8c0eb9", size = 295633, upload_time = "2025-04-03T23:51:02.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/aa/7c9db8edd626f1a7d99d09ef7926f6f4fb34d5f9fa00dc394afdfe8e2a80/azure_core-1.33.0.tar.gz", hash = "sha256:f367aa07b5e3005fec2c1e184b882b0b039910733907d001c20fb08ebb8c0eb9", size = 295633, upload-time = "2025-04-03T23:51:02.058Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/b7/76b7e144aa53bd206bf1ce34fa75350472c3f69bf30e5c8c18bc9881035d/azure_core-1.33.0-py3-none-any.whl", hash = "sha256:9b5b6d0223a1d38c37500e6971118c1e0f13f54951e6893968b38910bc9cda8f", size = 207071, upload_time = "2025-04-03T23:51:03.806Z" }, + { url = "https://files.pythonhosted.org/packages/07/b7/76b7e144aa53bd206bf1ce34fa75350472c3f69bf30e5c8c18bc9881035d/azure_core-1.33.0-py3-none-any.whl", hash = "sha256:9b5b6d0223a1d38c37500e6971118c1e0f13f54951e6893968b38910bc9cda8f", size = 207071, upload-time = "2025-04-03T23:51:03.806Z" }, ] [[package]] @@ -172,9 +172,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/a1/f1a683672e7a88ea0e3119f57b6c7843ed52650fdcac8bfa66ed84e86e40/azure_identity-1.21.0.tar.gz", hash = "sha256:ea22ce6e6b0f429bc1b8d9212d5b9f9877bd4c82f1724bfa910760612c07a9a6", size = 266445, upload_time = "2025-03-11T20:53:07.463Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/a1/f1a683672e7a88ea0e3119f57b6c7843ed52650fdcac8bfa66ed84e86e40/azure_identity-1.21.0.tar.gz", hash = "sha256:ea22ce6e6b0f429bc1b8d9212d5b9f9877bd4c82f1724bfa910760612c07a9a6", size = 266445, upload-time = "2025-03-11T20:53:07.463Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/9f/1f9f3ef4f49729ee207a712a5971a9ca747f2ca47d9cbf13cf6953e3478a/azure_identity-1.21.0-py3-none-any.whl", hash = "sha256:258ea6325537352440f71b35c3dffe9d240eae4a5126c1b7ce5efd5766bd9fd9", size = 189190, upload_time = "2025-03-11T20:53:09.197Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9f/1f9f3ef4f49729ee207a712a5971a9ca747f2ca47d9cbf13cf6953e3478a/azure_identity-1.21.0-py3-none-any.whl", hash = "sha256:258ea6325537352440f71b35c3dffe9d240eae4a5126c1b7ce5efd5766bd9fd9", size = 189190, upload-time = "2025-03-11T20:53:09.197Z" }, ] [[package]] @@ -187,9 +187,9 @@ dependencies = [ { name = "isodate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/f764536c25cc3829d36857167f03933ce9aee2262293179075439f3cd3ad/azure_storage_blob-12.25.1.tar.gz", hash = "sha256:4f294ddc9bc47909ac66b8934bd26b50d2000278b10ad82cc109764fdc6e0e3b", size = 570541, upload_time = "2025-03-27T17:13:05.424Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/f764536c25cc3829d36857167f03933ce9aee2262293179075439f3cd3ad/azure_storage_blob-12.25.1.tar.gz", hash = "sha256:4f294ddc9bc47909ac66b8934bd26b50d2000278b10ad82cc109764fdc6e0e3b", size = 570541, upload-time = "2025-03-27T17:13:05.424Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/57/33/085d9352d416e617993821b9d9488222fbb559bc15c3641d6cbd6d16d236/azure_storage_blob-12.25.1-py3-none-any.whl", hash = "sha256:1f337aab12e918ec3f1b638baada97550673911c4ceed892acc8e4e891b74167", size = 406990, upload_time = "2025-03-27T17:13:06.879Z" }, + { url = "https://files.pythonhosted.org/packages/57/33/085d9352d416e617993821b9d9488222fbb559bc15c3641d6cbd6d16d236/azure_storage_blob-12.25.1-py3-none-any.whl", hash = "sha256:1f337aab12e918ec3f1b638baada97550673911c4ceed892acc8e4e891b74167", size = 406990, upload-time = "2025-03-27T17:13:06.879Z" }, ] [[package]] @@ -199,29 +199,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/49/f8b8eb7c8e98e28e0cca1b41988932025d59a602fb2075f737cbaecf764d/casadi-3.7.0.tar.gz", hash = "sha256:21254f17eb5551c4a938641cc1b815ff3da27271ab2c36e44a3e90ec50ba471f", size = 6027731, upload_time = "2025-03-29T22:31:31.309Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/49/f8b8eb7c8e98e28e0cca1b41988932025d59a602fb2075f737cbaecf764d/casadi-3.7.0.tar.gz", hash = "sha256:21254f17eb5551c4a938641cc1b815ff3da27271ab2c36e44a3e90ec50ba471f", size = 6027731, upload-time = "2025-03-29T22:31:31.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/cf/7d1dc6b16f690f1fac22a0a43684de3a03b3f631a1d8c25393f320b5f971/casadi-3.7.0-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:50bba75bb9575baa09a2e25ce3c1968803320c49b9dcc218139321dab02f0400", size = 48302536, upload_time = "2025-03-30T07:31:37.067Z" }, - { url = "https://files.pythonhosted.org/packages/74/d2/f69156aaf4545543995c791a0f97551b66f9c9bc8d92361973ee6378ad25/casadi-3.7.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:8f138bf370603f6f820385d78882f840168e8e0866bea5de08b6b54a6e22c093", size = 44713683, upload_time = "2025-03-30T07:33:37.446Z" }, - { url = "https://files.pythonhosted.org/packages/2e/3b/13a7d92c27543e0a2eb559f2a5acc7278f1a2ef0217772b09df50ac1c2dc/casadi-3.7.0-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:4d844a1281c7ff527115d27ee8f2c58315d0008cac835656323a6d502de27010", size = 45518255, upload_time = "2025-03-30T07:36:03.844Z" }, - { url = "https://files.pythonhosted.org/packages/bb/88/00adcb26a0c313ebeaa2db8b2691e37418d6b7c3e06b4a90a3ac9380b91b/casadi-3.7.0-cp311-none-manylinux2014_i686.whl", hash = "sha256:b8640486db98b75a75eb05b6191d5a7f0d44774c36c07c7da327d4d740914284", size = 74329105, upload_time = "2025-03-30T07:39:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/64/b31fd4ce5b93f97fd16a9ba7ce8d4a8d36334a518f1ad00525340db31ff4/casadi-3.7.0-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:65d9a61660c75ff8f0c30abf52e2f9cefcdee1cbb9301e26678f64116f509ee7", size = 77330636, upload_time = "2025-03-30T07:46:55.954Z" }, - { url = "https://files.pythonhosted.org/packages/c4/25/79bb6b866a7005186ccdfcb89e01032885a9efa90acfe2d079253685622b/casadi-3.7.0-cp311-none-win_amd64.whl", hash = "sha256:391b5ee886cde28bf813e820958bfcdef98314bd367a93c95e7bef1bf713b886", size = 50949252, upload_time = "2025-03-30T07:49:05.348Z" }, - { url = "https://files.pythonhosted.org/packages/b8/27/6ca4c831d9131eb15ca34346398c1379a577363c264ad47983c1be65b3e1/casadi-3.7.0-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:ceb4d67c1c5cdf80f233866fcdcaf7d77523115983081fe909b70fe89fd7b708", size = 48303214, upload_time = "2025-03-30T07:51:05.246Z" }, - { url = "https://files.pythonhosted.org/packages/b2/cb/cd8be8aeac6453e2aad96793101fa287f54f86b643d542375969738930c3/casadi-3.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:83dbc27125044e7600b04af648f24e6924d46e81afe6ff088218e4b7e9a37f08", size = 44713758, upload_time = "2025-03-30T07:52:44.766Z" }, - { url = "https://files.pythonhosted.org/packages/a4/3d/c8a392eac772b1537f63ee16b57be87bcdef9d9bc9530b54c95b1a122960/casadi-3.7.0-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:53731171b41ca0640b76657d2320b929634a137ff30f12f985196df35411885b", size = 45517623, upload_time = "2025-03-30T07:54:25.318Z" }, - { url = "https://files.pythonhosted.org/packages/68/d2/2acd3b8cf8fa25bca342cd539f69dc94b0fa1bf3acaa30f13848fa0f31ee/casadi-3.7.0-cp312-none-manylinux2014_i686.whl", hash = "sha256:6b30fb73d8c140fbbe51e60d00412aaefe5a7b775257a422ea244f423bd2351c", size = 74319729, upload_time = "2025-03-30T07:57:18.329Z" }, - { url = "https://files.pythonhosted.org/packages/0f/35/ec351423c854a74884218501a431e018c6eee79461dde8e91f9ce6b4e2b5/casadi-3.7.0-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:b795371bf09ec0bfd22eaa0a1e81ce2cd3ecd811c9d370b98f6853b87e8397c9", size = 77323153, upload_time = "2025-03-30T08:00:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4b/ab605b5948795fe15b5930ecc5ac7ba72ebad116a9003c0117421cbe34a8/casadi-3.7.0-cp312-none-win_amd64.whl", hash = "sha256:e77173aad67b817ebf4320c31cef37c7d666f9895bc5970f3370b2ae6fdad587", size = 50946897, upload_time = "2025-03-30T08:02:37.836Z" }, + { url = "https://files.pythonhosted.org/packages/83/cf/7d1dc6b16f690f1fac22a0a43684de3a03b3f631a1d8c25393f320b5f971/casadi-3.7.0-cp311-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:50bba75bb9575baa09a2e25ce3c1968803320c49b9dcc218139321dab02f0400", size = 48302536, upload-time = "2025-03-30T07:31:37.067Z" }, + { url = "https://files.pythonhosted.org/packages/74/d2/f69156aaf4545543995c791a0f97551b66f9c9bc8d92361973ee6378ad25/casadi-3.7.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:8f138bf370603f6f820385d78882f840168e8e0866bea5de08b6b54a6e22c093", size = 44713683, upload-time = "2025-03-30T07:33:37.446Z" }, + { url = "https://files.pythonhosted.org/packages/2e/3b/13a7d92c27543e0a2eb559f2a5acc7278f1a2ef0217772b09df50ac1c2dc/casadi-3.7.0-cp311-none-manylinux2014_aarch64.whl", hash = "sha256:4d844a1281c7ff527115d27ee8f2c58315d0008cac835656323a6d502de27010", size = 45518255, upload-time = "2025-03-30T07:36:03.844Z" }, + { url = "https://files.pythonhosted.org/packages/bb/88/00adcb26a0c313ebeaa2db8b2691e37418d6b7c3e06b4a90a3ac9380b91b/casadi-3.7.0-cp311-none-manylinux2014_i686.whl", hash = "sha256:b8640486db98b75a75eb05b6191d5a7f0d44774c36c07c7da327d4d740914284", size = 74329105, upload-time = "2025-03-30T07:39:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/64/b31fd4ce5b93f97fd16a9ba7ce8d4a8d36334a518f1ad00525340db31ff4/casadi-3.7.0-cp311-none-manylinux2014_x86_64.whl", hash = "sha256:65d9a61660c75ff8f0c30abf52e2f9cefcdee1cbb9301e26678f64116f509ee7", size = 77330636, upload-time = "2025-03-30T07:46:55.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/25/79bb6b866a7005186ccdfcb89e01032885a9efa90acfe2d079253685622b/casadi-3.7.0-cp311-none-win_amd64.whl", hash = "sha256:391b5ee886cde28bf813e820958bfcdef98314bd367a93c95e7bef1bf713b886", size = 50949252, upload-time = "2025-03-30T07:49:05.348Z" }, + { url = "https://files.pythonhosted.org/packages/b8/27/6ca4c831d9131eb15ca34346398c1379a577363c264ad47983c1be65b3e1/casadi-3.7.0-cp312-none-macosx_10_13_x86_64.macosx_10_13_intel.whl", hash = "sha256:ceb4d67c1c5cdf80f233866fcdcaf7d77523115983081fe909b70fe89fd7b708", size = 48303214, upload-time = "2025-03-30T07:51:05.246Z" }, + { url = "https://files.pythonhosted.org/packages/b2/cb/cd8be8aeac6453e2aad96793101fa287f54f86b643d542375969738930c3/casadi-3.7.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:83dbc27125044e7600b04af648f24e6924d46e81afe6ff088218e4b7e9a37f08", size = 44713758, upload-time = "2025-03-30T07:52:44.766Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3d/c8a392eac772b1537f63ee16b57be87bcdef9d9bc9530b54c95b1a122960/casadi-3.7.0-cp312-none-manylinux2014_aarch64.whl", hash = "sha256:53731171b41ca0640b76657d2320b929634a137ff30f12f985196df35411885b", size = 45517623, upload-time = "2025-03-30T07:54:25.318Z" }, + { url = "https://files.pythonhosted.org/packages/68/d2/2acd3b8cf8fa25bca342cd539f69dc94b0fa1bf3acaa30f13848fa0f31ee/casadi-3.7.0-cp312-none-manylinux2014_i686.whl", hash = "sha256:6b30fb73d8c140fbbe51e60d00412aaefe5a7b775257a422ea244f423bd2351c", size = 74319729, upload-time = "2025-03-30T07:57:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/0f/35/ec351423c854a74884218501a431e018c6eee79461dde8e91f9ce6b4e2b5/casadi-3.7.0-cp312-none-manylinux2014_x86_64.whl", hash = "sha256:b795371bf09ec0bfd22eaa0a1e81ce2cd3ecd811c9d370b98f6853b87e8397c9", size = 77323153, upload-time = "2025-03-30T08:00:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4b/ab605b5948795fe15b5930ecc5ac7ba72ebad116a9003c0117421cbe34a8/casadi-3.7.0-cp312-none-win_amd64.whl", hash = "sha256:e77173aad67b817ebf4320c31cef37c7d666f9895bc5970f3370b2ae6fdad587", size = 50946897, upload-time = "2025-03-30T08:02:37.836Z" }, ] [[package]] name = "certifi" version = "2025.4.26" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload_time = "2025-04-26T02:12:29.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload_time = "2025-04-26T02:12:27.662Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, ] [[package]] @@ -231,66 +231,66 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload_time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } wheels = [ - { 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, upload_time = "2024-09-04T20:43:51.124Z" }, - { 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, upload_time = "2024-09-04T20:43:52.872Z" }, - { 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, upload_time = "2024-09-04T20:43:56.123Z" }, - { 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, upload_time = "2024-09-04T20:43:57.891Z" }, - { 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, upload_time = "2024-09-04T20:44:00.18Z" }, - { 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, upload_time = "2024-09-04T20:44:01.585Z" }, - { 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, upload_time = "2024-09-04T20:44:03.467Z" }, - { 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, upload_time = "2024-09-04T20:44:05.023Z" }, - { 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, upload_time = "2024-09-04T20:44:06.444Z" }, - { 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, upload_time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload_time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload_time = "2024-09-04T20:44:10.873Z" }, - { 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, upload_time = "2024-09-04T20:44:12.232Z" }, - { 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, upload_time = "2024-09-04T20:44:13.739Z" }, - { 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, upload_time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload_time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload_time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload_time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload_time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload_time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload_time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload_time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload_time = "2024-09-04T20:44:27.578Z" }, + { 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, upload-time = "2024-09-04T20:43:51.124Z" }, + { 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, upload-time = "2024-09-04T20:43:52.872Z" }, + { 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, upload-time = "2024-09-04T20:43:56.123Z" }, + { 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, upload-time = "2024-09-04T20:43:57.891Z" }, + { 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, upload-time = "2024-09-04T20:44:00.18Z" }, + { 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, upload-time = "2024-09-04T20:44:01.585Z" }, + { 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, upload-time = "2024-09-04T20:44:03.467Z" }, + { 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, upload-time = "2024-09-04T20:44:05.023Z" }, + { 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, upload-time = "2024-09-04T20:44:06.444Z" }, + { 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, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, + { 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, upload-time = "2024-09-04T20:44:12.232Z" }, + { 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, upload-time = "2024-09-04T20:44:13.739Z" }, + { 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, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload_time = "2024-12-24T18:12:35.43Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995, upload_time = "2024-12-24T18:10:12.838Z" }, - { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471, upload_time = "2024-12-24T18:10:14.101Z" }, - { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831, upload_time = "2024-12-24T18:10:15.512Z" }, - { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335, upload_time = "2024-12-24T18:10:18.369Z" }, - { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862, upload_time = "2024-12-24T18:10:19.743Z" }, - { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673, upload_time = "2024-12-24T18:10:21.139Z" }, - { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211, upload_time = "2024-12-24T18:10:22.382Z" }, - { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039, upload_time = "2024-12-24T18:10:24.802Z" }, - { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939, upload_time = "2024-12-24T18:10:26.124Z" }, - { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075, upload_time = "2024-12-24T18:10:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340, upload_time = "2024-12-24T18:10:32.679Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205, upload_time = "2024-12-24T18:10:34.724Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441, upload_time = "2024-12-24T18:10:37.574Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload_time = "2024-12-24T18:10:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload_time = "2024-12-24T18:10:44.272Z" }, - { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload_time = "2024-12-24T18:10:45.492Z" }, - { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload_time = "2024-12-24T18:10:47.898Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload_time = "2024-12-24T18:10:50.589Z" }, - { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload_time = "2024-12-24T18:10:52.541Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload_time = "2024-12-24T18:10:53.789Z" }, - { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload_time = "2024-12-24T18:10:55.048Z" }, - { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload_time = "2024-12-24T18:10:57.647Z" }, - { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload_time = "2024-12-24T18:10:59.43Z" }, - { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload_time = "2024-12-24T18:11:00.676Z" }, - { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload_time = "2024-12-24T18:11:01.952Z" }, - { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload_time = "2024-12-24T18:11:03.142Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload_time = "2024-12-24T18:12:32.852Z" }, + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995, upload-time = "2024-12-24T18:10:12.838Z" }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471, upload-time = "2024-12-24T18:10:14.101Z" }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831, upload-time = "2024-12-24T18:10:15.512Z" }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335, upload-time = "2024-12-24T18:10:18.369Z" }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862, upload-time = "2024-12-24T18:10:19.743Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673, upload-time = "2024-12-24T18:10:21.139Z" }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211, upload-time = "2024-12-24T18:10:22.382Z" }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039, upload-time = "2024-12-24T18:10:24.802Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939, upload-time = "2024-12-24T18:10:26.124Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075, upload-time = "2024-12-24T18:10:30.027Z" }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340, upload-time = "2024-12-24T18:10:32.679Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205, upload-time = "2024-12-24T18:10:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441, upload-time = "2024-12-24T18:10:37.574Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, ] [[package]] @@ -300,36 +300,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload_time = "2024-12-21T18:38:44.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload_time = "2024-12-21T18:38:41.666Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, ] [[package]] name = "cloudpickle" version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload_time = "2025-01-14T17:02:05.085Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/39/069100b84d7418bc358d81669d5748efb14b9cceacd2f9c75f550424132f/cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64", size = 22113, upload-time = "2025-01-14T17:02:05.085Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload_time = "2025-01-14T17:02:02.417Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e", size = 20992, upload-time = "2025-01-14T17:02:02.417Z" }, ] [[package]] name = "codespell" version = "2.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/e0/709453393c0ea77d007d907dd436b3ee262e28b30995ea1aa36c6ffbccaf/codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5", size = 344740, upload_time = "2025-01-28T18:52:39.411Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/e0/709453393c0ea77d007d907dd436b3ee262e28b30995ea1aa36c6ffbccaf/codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5", size = 344740, upload-time = "2025-01-28T18:52:39.411Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/01/b394922252051e97aab231d416c86da3d8a6d781eeadcdca1082867de64e/codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425", size = 344501, upload_time = "2025-01-28T18:52:37.057Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/b394922252051e97aab231d416c86da3d8a6d781eeadcdca1082867de64e/codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425", size = 344501, upload-time = "2025-01-28T18:52:37.057Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] @@ -339,61 +339,61 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload_time = "2025-04-15T17:47:53.79Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload_time = "2025-04-15T17:35:54.473Z" }, - { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload_time = "2025-04-15T17:35:58.283Z" }, - { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload_time = "2025-04-15T17:36:03.235Z" }, - { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload_time = "2025-04-15T17:36:08.275Z" }, - { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload_time = "2025-04-15T17:36:13.29Z" }, - { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload_time = "2025-04-15T17:36:18.329Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload_time = "2025-04-15T17:36:33.878Z" }, - { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload_time = "2025-04-15T17:36:51.295Z" }, - { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload_time = "2025-04-15T17:36:55.002Z" }, - { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload_time = "2025-04-15T17:36:58.576Z" }, - { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload_time = "2025-04-15T17:37:03.105Z" }, - { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload_time = "2025-04-15T17:37:07.026Z" }, - { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload_time = "2025-04-15T17:37:11.481Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload_time = "2025-04-15T17:37:18.212Z" }, - { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload_time = "2025-04-15T17:37:22.76Z" }, - { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload_time = "2025-04-15T17:37:33.001Z" }, - { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload_time = "2025-04-15T17:37:48.64Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload_time = "2025-04-15T17:38:06.7Z" }, - { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload_time = "2025-04-15T17:38:10.338Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload_time = "2025-04-15T17:38:14.239Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload_time = "2025-04-15T17:45:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload_time = "2025-04-15T17:45:20.166Z" }, - { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload_time = "2025-04-15T17:45:24.794Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, ] [[package]] name = "coverage" version = "7.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872, upload_time = "2025-03-30T20:36:45.376Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872, upload-time = "2025-03-30T20:36:45.376Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493, upload_time = "2025-03-30T20:35:12.286Z" }, - { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921, upload_time = "2025-03-30T20:35:14.18Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556, upload_time = "2025-03-30T20:35:15.616Z" }, - { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245, upload_time = "2025-03-30T20:35:18.648Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032, upload_time = "2025-03-30T20:35:20.131Z" }, - { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679, upload_time = "2025-03-30T20:35:21.636Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852, upload_time = "2025-03-30T20:35:23.525Z" }, - { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389, upload_time = "2025-03-30T20:35:25.09Z" }, - { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997, upload_time = "2025-03-30T20:35:26.914Z" }, - { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911, upload_time = "2025-03-30T20:35:28.498Z" }, - { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684, upload_time = "2025-03-30T20:35:29.959Z" }, - { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935, upload_time = "2025-03-30T20:35:31.912Z" }, - { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994, upload_time = "2025-03-30T20:35:33.455Z" }, - { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885, upload_time = "2025-03-30T20:35:35.354Z" }, - { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142, upload_time = "2025-03-30T20:35:37.121Z" }, - { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906, upload_time = "2025-03-30T20:35:39.07Z" }, - { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124, upload_time = "2025-03-30T20:35:40.598Z" }, - { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317, upload_time = "2025-03-30T20:35:42.204Z" }, - { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170, upload_time = "2025-03-30T20:35:44.216Z" }, - { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969, upload_time = "2025-03-30T20:35:45.797Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443, upload_time = "2025-03-30T20:36:41.959Z" }, - { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435, upload_time = "2025-03-30T20:36:43.61Z" }, + { url = "https://files.pythonhosted.org/packages/2b/77/074d201adb8383addae5784cb8e2dac60bb62bfdf28b2b10f3a3af2fda47/coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", size = 211493, upload-time = "2025-03-30T20:35:12.286Z" }, + { url = "https://files.pythonhosted.org/packages/a9/89/7a8efe585750fe59b48d09f871f0e0c028a7b10722b2172dfe021fa2fdd4/coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", size = 211921, upload-time = "2025-03-30T20:35:14.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ef/96a90c31d08a3f40c49dbe897df4f1fd51fb6583821a1a1c5ee30cc8f680/coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", size = 244556, upload-time = "2025-03-30T20:35:15.616Z" }, + { url = "https://files.pythonhosted.org/packages/89/97/dcd5c2ce72cee9d7b0ee8c89162c24972fb987a111b92d1a3d1d19100c61/coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", size = 242245, upload-time = "2025-03-30T20:35:18.648Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7b/b63cbb44096141ed435843bbb251558c8e05cc835c8da31ca6ffb26d44c0/coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", size = 244032, upload-time = "2025-03-30T20:35:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/97/e3/7fa8c2c00a1ef530c2a42fa5df25a6971391f92739d83d67a4ee6dcf7a02/coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", size = 243679, upload-time = "2025-03-30T20:35:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b3/e0a59d8df9150c8a0c0841d55d6568f0a9195692136c44f3d21f1842c8f6/coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", size = 241852, upload-time = "2025-03-30T20:35:23.525Z" }, + { url = "https://files.pythonhosted.org/packages/9b/82/db347ccd57bcef150c173df2ade97976a8367a3be7160e303e43dd0c795f/coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", size = 242389, upload-time = "2025-03-30T20:35:25.09Z" }, + { url = "https://files.pythonhosted.org/packages/21/f6/3f7d7879ceb03923195d9ff294456241ed05815281f5254bc16ef71d6a20/coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", size = 213997, upload-time = "2025-03-30T20:35:26.914Z" }, + { url = "https://files.pythonhosted.org/packages/28/87/021189643e18ecf045dbe1e2071b2747901f229df302de01c998eeadf146/coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", size = 214911, upload-time = "2025-03-30T20:35:28.498Z" }, + { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684, upload-time = "2025-03-30T20:35:29.959Z" }, + { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935, upload-time = "2025-03-30T20:35:31.912Z" }, + { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994, upload-time = "2025-03-30T20:35:33.455Z" }, + { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885, upload-time = "2025-03-30T20:35:35.354Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142, upload-time = "2025-03-30T20:35:37.121Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906, upload-time = "2025-03-30T20:35:39.07Z" }, + { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124, upload-time = "2025-03-30T20:35:40.598Z" }, + { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317, upload-time = "2025-03-30T20:35:42.204Z" }, + { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170, upload-time = "2025-03-30T20:35:44.216Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969, upload-time = "2025-03-30T20:35:45.797Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f1/1da77bb4c920aa30e82fa9b6ea065da3467977c2e5e032e38e66f1c57ffd/coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", size = 203443, upload-time = "2025-03-30T20:36:41.959Z" }, + { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435, upload-time = "2025-03-30T20:36:43.61Z" }, ] [package.optional-dependencies] @@ -405,7 +405,7 @@ toml = [ name = "crcmod" version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/b0/e595ce2a2527e169c3bcd6c33d2473c1918e0b7f6826a043ca1245dd4e5b/crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e", size = 89670, upload_time = "2010-06-27T14:35:29.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/b0/e595ce2a2527e169c3bcd6c33d2473c1918e0b7f6826a043ca1245dd4e5b/crcmod-1.7.tar.gz", hash = "sha256:dc7051a0db5f2bd48665a990d3ec1cc305a466a77358ca4492826f41f283601e", size = 89670, upload-time = "2010-06-27T14:35:29.538Z" } [[package]] name = "cryptography" @@ -414,87 +414,87 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", size = 686989, upload_time = "2024-10-18T15:58:32.918Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", size = 686989, upload-time = "2024-10-18T15:58:32.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/f3/01fdf26701a26f4b4dbc337a26883ad5bccaa6f1bbbdd29cd89e22f18a1c/cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", size = 6225303, upload_time = "2024-10-18T15:57:36.753Z" }, - { url = "https://files.pythonhosted.org/packages/a3/01/4896f3d1b392025d4fcbecf40fdea92d3df8662123f6835d0af828d148fd/cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", size = 3760905, upload_time = "2024-10-18T15:57:39.166Z" }, - { url = "https://files.pythonhosted.org/packages/0a/be/f9a1f673f0ed4b7f6c643164e513dbad28dd4f2dcdf5715004f172ef24b6/cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", size = 3977271, upload_time = "2024-10-18T15:57:41.227Z" }, - { url = "https://files.pythonhosted.org/packages/4e/49/80c3a7b5514d1b416d7350830e8c422a4d667b6d9b16a9392ebfd4a5388a/cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", size = 3746606, upload_time = "2024-10-18T15:57:42.903Z" }, - { url = "https://files.pythonhosted.org/packages/0e/16/a28ddf78ac6e7e3f25ebcef69ab15c2c6be5ff9743dd0709a69a4f968472/cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", size = 3986484, upload_time = "2024-10-18T15:57:45.434Z" }, - { url = "https://files.pythonhosted.org/packages/01/f5/69ae8da70c19864a32b0315049866c4d411cce423ec169993d0434218762/cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", size = 3852131, upload_time = "2024-10-18T15:57:47.267Z" }, - { url = "https://files.pythonhosted.org/packages/fd/db/e74911d95c040f9afd3612b1f732e52b3e517cb80de8bf183be0b7d413c6/cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", size = 4075647, upload_time = "2024-10-18T15:57:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/56/48/7b6b190f1462818b324e674fa20d1d5ef3e24f2328675b9b16189cbf0b3c/cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", size = 2623873, upload_time = "2024-10-18T15:57:51.822Z" }, - { url = "https://files.pythonhosted.org/packages/eb/b1/0ebff61a004f7f89e7b65ca95f2f2375679d43d0290672f7713ee3162aff/cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", size = 3068039, upload_time = "2024-10-18T15:57:54.426Z" }, - { url = "https://files.pythonhosted.org/packages/30/d5/c8b32c047e2e81dd172138f772e81d852c51f0f2ad2ae8a24f1122e9e9a7/cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", size = 6222984, upload_time = "2024-10-18T15:57:56.174Z" }, - { url = "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", size = 3762968, upload_time = "2024-10-18T15:57:58.206Z" }, - { url = "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", size = 3977754, upload_time = "2024-10-18T15:58:00.683Z" }, - { url = "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", size = 3749458, upload_time = "2024-10-18T15:58:02.225Z" }, - { url = "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", size = 3988220, upload_time = "2024-10-18T15:58:04.331Z" }, - { url = "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", size = 3853898, upload_time = "2024-10-18T15:58:06.113Z" }, - { url = "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", size = 4076592, upload_time = "2024-10-18T15:58:08.673Z" }, - { url = "https://files.pythonhosted.org/packages/81/1e/ffcc41b3cebd64ca90b28fd58141c5f68c83d48563c88333ab660e002cd3/cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", size = 2623145, upload_time = "2024-10-18T15:58:10.264Z" }, - { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026, upload_time = "2024-10-18T15:58:11.916Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/01fdf26701a26f4b4dbc337a26883ad5bccaa6f1bbbdd29cd89e22f18a1c/cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", size = 6225303, upload-time = "2024-10-18T15:57:36.753Z" }, + { url = "https://files.pythonhosted.org/packages/a3/01/4896f3d1b392025d4fcbecf40fdea92d3df8662123f6835d0af828d148fd/cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", size = 3760905, upload-time = "2024-10-18T15:57:39.166Z" }, + { url = "https://files.pythonhosted.org/packages/0a/be/f9a1f673f0ed4b7f6c643164e513dbad28dd4f2dcdf5715004f172ef24b6/cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", size = 3977271, upload-time = "2024-10-18T15:57:41.227Z" }, + { url = "https://files.pythonhosted.org/packages/4e/49/80c3a7b5514d1b416d7350830e8c422a4d667b6d9b16a9392ebfd4a5388a/cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", size = 3746606, upload-time = "2024-10-18T15:57:42.903Z" }, + { url = "https://files.pythonhosted.org/packages/0e/16/a28ddf78ac6e7e3f25ebcef69ab15c2c6be5ff9743dd0709a69a4f968472/cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", size = 3986484, upload-time = "2024-10-18T15:57:45.434Z" }, + { url = "https://files.pythonhosted.org/packages/01/f5/69ae8da70c19864a32b0315049866c4d411cce423ec169993d0434218762/cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", size = 3852131, upload-time = "2024-10-18T15:57:47.267Z" }, + { url = "https://files.pythonhosted.org/packages/fd/db/e74911d95c040f9afd3612b1f732e52b3e517cb80de8bf183be0b7d413c6/cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", size = 4075647, upload-time = "2024-10-18T15:57:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/56/48/7b6b190f1462818b324e674fa20d1d5ef3e24f2328675b9b16189cbf0b3c/cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", size = 2623873, upload-time = "2024-10-18T15:57:51.822Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b1/0ebff61a004f7f89e7b65ca95f2f2375679d43d0290672f7713ee3162aff/cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", size = 3068039, upload-time = "2024-10-18T15:57:54.426Z" }, + { url = "https://files.pythonhosted.org/packages/30/d5/c8b32c047e2e81dd172138f772e81d852c51f0f2ad2ae8a24f1122e9e9a7/cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", size = 6222984, upload-time = "2024-10-18T15:57:56.174Z" }, + { url = "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", size = 3762968, upload-time = "2024-10-18T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", size = 3977754, upload-time = "2024-10-18T15:58:00.683Z" }, + { url = "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", size = 3749458, upload-time = "2024-10-18T15:58:02.225Z" }, + { url = "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", size = 3988220, upload-time = "2024-10-18T15:58:04.331Z" }, + { url = "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", size = 3853898, upload-time = "2024-10-18T15:58:06.113Z" }, + { url = "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", size = 4076592, upload-time = "2024-10-18T15:58:08.673Z" }, + { url = "https://files.pythonhosted.org/packages/81/1e/ffcc41b3cebd64ca90b28fd58141c5f68c83d48563c88333ab660e002cd3/cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", size = 2623145, upload-time = "2024-10-18T15:58:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/87/5c/3dab83cc4aba1f4b0e733e3f0c3e7d4386440d660ba5b1e3ff995feb734d/cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", size = 3068026, upload-time = "2024-10-18T15:58:11.916Z" }, ] [[package]] name = "cycler" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload_time = "2023-10-07T05:32:18.335Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload_time = "2023-10-07T05:32:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] [[package]] name = "cython" version = "3.0.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617, upload_time = "2025-02-11T09:05:50.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617, upload-time = "2025-02-11T09:05:50.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430, upload_time = "2025-02-11T09:06:47.253Z" }, - { url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055, upload_time = "2025-02-11T09:06:50.807Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874, upload_time = "2025-02-11T09:06:54.806Z" }, - { url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129, upload_time = "2025-02-11T09:06:58.152Z" }, - { url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936, upload_time = "2025-02-11T09:07:01.592Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066, upload_time = "2025-02-11T09:07:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935, upload_time = "2025-02-11T09:07:06.947Z" }, - { url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337, upload_time = "2025-02-11T09:07:10.087Z" }, - { url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717, upload_time = "2025-02-11T09:07:12.405Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337, upload_time = "2025-02-11T09:07:14.979Z" }, - { url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692, upload_time = "2025-02-11T09:07:17.45Z" }, - { url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057, upload_time = "2025-02-11T09:07:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493, upload_time = "2025-02-11T09:07:25.183Z" }, - { url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859, upload_time = "2025-02-11T09:07:27.634Z" }, - { url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428, upload_time = "2025-02-11T09:07:30.719Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755, upload_time = "2025-02-11T09:07:36.021Z" }, - { url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640, upload_time = "2025-02-11T09:05:45.648Z" }, + { url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430, upload-time = "2025-02-11T09:06:47.253Z" }, + { url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055, upload-time = "2025-02-11T09:06:50.807Z" }, + { url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874, upload-time = "2025-02-11T09:06:54.806Z" }, + { url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129, upload-time = "2025-02-11T09:06:58.152Z" }, + { url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936, upload-time = "2025-02-11T09:07:01.592Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066, upload-time = "2025-02-11T09:07:03.961Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935, upload-time = "2025-02-11T09:07:06.947Z" }, + { url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337, upload-time = "2025-02-11T09:07:10.087Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717, upload-time = "2025-02-11T09:07:12.405Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337, upload-time = "2025-02-11T09:07:14.979Z" }, + { url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692, upload-time = "2025-02-11T09:07:17.45Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057, upload-time = "2025-02-11T09:07:22.106Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493, upload-time = "2025-02-11T09:07:25.183Z" }, + { url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859, upload-time = "2025-02-11T09:07:27.634Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428, upload-time = "2025-02-11T09:07:30.719Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755, upload-time = "2025-02-11T09:07:36.021Z" }, + { url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640, upload-time = "2025-02-11T09:05:45.648Z" }, ] [[package]] name = "dbus-next" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload_time = "2021-07-25T22:11:28.398Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload_time = "2021-07-25T22:11:25.466Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, ] [[package]] name = "dictdiffer" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/7b/35cbccb7effc5d7e40f4c55e2b79399e1853041997fcda15c9ff160abba0/dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578", size = 31513, upload_time = "2021-07-22T13:24:29.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/7b/35cbccb7effc5d7e40f4c55e2b79399e1853041997fcda15c9ff160abba0/dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578", size = 31513, upload-time = "2021-07-22T13:24:29.276Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/ef/4cb333825d10317a36a1154341ba37e6e9c087bac99c1990ef07ffdb376f/dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595", size = 16754, upload_time = "2021-07-22T13:24:26.783Z" }, + { url = "https://files.pythonhosted.org/packages/47/ef/4cb333825d10317a36a1154341ba37e6e9c087bac99c1990ef07ffdb376f/dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595", size = 16754, upload-time = "2021-07-22T13:24:26.783Z" }, ] [[package]] name = "dnspython" version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload_time = "2024-10-05T20:14:59.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197, upload-time = "2024-10-05T20:14:59.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload_time = "2024-10-05T20:14:57.687Z" }, + { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632, upload-time = "2024-10-05T20:14:57.687Z" }, ] [[package]] @@ -506,111 +506,111 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657, upload_time = "2024-04-17T08:15:56.338Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657, upload-time = "2024-04-17T08:15:56.338Z" }, ] [[package]] name = "execnet" version = "2.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524, upload_time = "2024-04-08T09:04:19.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524, upload-time = "2024-04-08T09:04:19.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612, upload_time = "2024-04-08T09:04:17.414Z" }, + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612, upload-time = "2024-04-08T09:04:17.414Z" }, ] [[package]] name = "farama-notifications" version = "0.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2e/2c/8384832b7a6b1fd6ba95bbdcae26e7137bb3eedc955c42fd5cdcc086cfbf/Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18", size = 2131, upload_time = "2023-02-27T18:28:41.047Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/2c/8384832b7a6b1fd6ba95bbdcae26e7137bb3eedc955c42fd5cdcc086cfbf/Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18", size = 2131, upload-time = "2023-02-27T18:28:41.047Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/2c/ffc08c54c05cdce6fbed2aeebc46348dbe180c6d2c541c7af7ba0aa5f5f8/Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae", size = 2511, upload_time = "2023-02-27T18:28:39.447Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/ffc08c54c05cdce6fbed2aeebc46348dbe180c6d2c541c7af7ba0aa5f5f8/Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae", size = 2511, upload-time = "2023-02-27T18:28:39.447Z" }, ] [[package]] name = "filelock" version = "3.18.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload_time = "2025-03-14T07:11:40.47Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload_time = "2025-03-14T07:11:39.145Z" }, + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, ] [[package]] name = "fonttools" version = "4.57.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/2d/a9a0b6e3a0cf6bd502e64fc16d894269011930cabfc89aee20d1635b1441/fonttools-4.57.0.tar.gz", hash = "sha256:727ece10e065be2f9dd239d15dd5d60a66e17eac11aea47d447f9f03fdbc42de", size = 3492448, upload_time = "2025-04-03T11:07:13.898Z" } +sdist = { url = "https://files.pythonhosted.org/packages/03/2d/a9a0b6e3a0cf6bd502e64fc16d894269011930cabfc89aee20d1635b1441/fonttools-4.57.0.tar.gz", hash = "sha256:727ece10e065be2f9dd239d15dd5d60a66e17eac11aea47d447f9f03fdbc42de", size = 3492448, upload-time = "2025-04-03T11:07:13.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/1f/e67c99aa3c6d3d2f93d956627e62a57ae0d35dc42f26611ea2a91053f6d6/fonttools-4.57.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3871349303bdec958360eedb619169a779956503ffb4543bb3e6211e09b647c4", size = 2757392, upload_time = "2025-04-03T11:05:45.715Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f1/f75770d0ddc67db504850898d96d75adde238c35313409bfcd8db4e4a5fe/fonttools-4.57.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c59375e85126b15a90fcba3443eaac58f3073ba091f02410eaa286da9ad80ed8", size = 2285609, upload_time = "2025-04-03T11:05:47.977Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d3/bc34e4953cb204bae0c50b527307dce559b810e624a733351a654cfc318e/fonttools-4.57.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967b65232e104f4b0f6370a62eb33089e00024f2ce143aecbf9755649421c683", size = 4873292, upload_time = "2025-04-03T11:05:49.921Z" }, - { url = "https://files.pythonhosted.org/packages/41/b8/d5933559303a4ab18c799105f4c91ee0318cc95db4a2a09e300116625e7a/fonttools-4.57.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39acf68abdfc74e19de7485f8f7396fa4d2418efea239b7061d6ed6a2510c746", size = 4902503, upload_time = "2025-04-03T11:05:52.17Z" }, - { url = "https://files.pythonhosted.org/packages/32/13/acb36bfaa316f481153ce78de1fa3926a8bad42162caa3b049e1afe2408b/fonttools-4.57.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d077f909f2343daf4495ba22bb0e23b62886e8ec7c109ee8234bdbd678cf344", size = 5077351, upload_time = "2025-04-03T11:05:54.162Z" }, - { url = "https://files.pythonhosted.org/packages/b5/23/6d383a2ca83b7516d73975d8cca9d81a01acdcaa5e4db8579e4f3de78518/fonttools-4.57.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:46370ac47a1e91895d40e9ad48effbe8e9d9db1a4b80888095bc00e7beaa042f", size = 5275067, upload_time = "2025-04-03T11:05:57.375Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ca/31b8919c6da0198d5d522f1d26c980201378c087bdd733a359a1e7485769/fonttools-4.57.0-cp311-cp311-win32.whl", hash = "sha256:ca2aed95855506b7ae94e8f1f6217b7673c929e4f4f1217bcaa236253055cb36", size = 2158263, upload_time = "2025-04-03T11:05:59.567Z" }, - { url = "https://files.pythonhosted.org/packages/13/4c/de2612ea2216eb45cfc8eb91a8501615dd87716feaf5f8fb65cbca576289/fonttools-4.57.0-cp311-cp311-win_amd64.whl", hash = "sha256:17168a4670bbe3775f3f3f72d23ee786bd965395381dfbb70111e25e81505b9d", size = 2204968, upload_time = "2025-04-03T11:06:02.16Z" }, - { url = "https://files.pythonhosted.org/packages/cb/98/d4bc42d43392982eecaaca117d79845734d675219680cd43070bb001bc1f/fonttools-4.57.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:889e45e976c74abc7256d3064aa7c1295aa283c6bb19810b9f8b604dfe5c7f31", size = 2751824, upload_time = "2025-04-03T11:06:03.782Z" }, - { url = "https://files.pythonhosted.org/packages/1a/62/7168030eeca3742fecf45f31e63b5ef48969fa230a672216b805f1d61548/fonttools-4.57.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0425c2e052a5f1516c94e5855dbda706ae5a768631e9fcc34e57d074d1b65b92", size = 2283072, upload_time = "2025-04-03T11:06:05.533Z" }, - { url = "https://files.pythonhosted.org/packages/5d/82/121a26d9646f0986ddb35fbbaf58ef791c25b59ecb63ffea2aab0099044f/fonttools-4.57.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44c26a311be2ac130f40a96769264809d3b0cb297518669db437d1cc82974888", size = 4788020, upload_time = "2025-04-03T11:06:07.249Z" }, - { url = "https://files.pythonhosted.org/packages/5b/26/e0f2fb662e022d565bbe280a3cfe6dafdaabf58889ff86fdef2d31ff1dde/fonttools-4.57.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c41ba992df5b8d680b89fd84c6a1f2aca2b9f1ae8a67400c8930cd4ea115f6", size = 4859096, upload_time = "2025-04-03T11:06:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/9e/44/9075e323347b1891cdece4b3f10a3b84a8f4c42a7684077429d9ce842056/fonttools-4.57.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea1e9e43ca56b0c12440a7c689b1350066595bebcaa83baad05b8b2675129d98", size = 4964356, upload_time = "2025-04-03T11:06:11.294Z" }, - { url = "https://files.pythonhosted.org/packages/48/28/caa8df32743462fb966be6de6a79d7f30393859636d7732e82efa09fbbb4/fonttools-4.57.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84fd56c78d431606332a0627c16e2a63d243d0d8b05521257d77c6529abe14d8", size = 5226546, upload_time = "2025-04-03T11:06:13.6Z" }, - { url = "https://files.pythonhosted.org/packages/f6/46/95ab0f0d2e33c5b1a4fc1c0efe5e286ba9359602c0a9907adb1faca44175/fonttools-4.57.0-cp312-cp312-win32.whl", hash = "sha256:f4376819c1c778d59e0a31db5dc6ede854e9edf28bbfa5b756604727f7f800ac", size = 2146776, upload_time = "2025-04-03T11:06:15.643Z" }, - { url = "https://files.pythonhosted.org/packages/06/5d/1be5424bb305880e1113631f49a55ea7c7da3a5fe02608ca7c16a03a21da/fonttools-4.57.0-cp312-cp312-win_amd64.whl", hash = "sha256:57e30241524879ea10cdf79c737037221f77cc126a8cdc8ff2c94d4a522504b9", size = 2193956, upload_time = "2025-04-03T11:06:17.534Z" }, - { url = "https://files.pythonhosted.org/packages/90/27/45f8957c3132917f91aaa56b700bcfc2396be1253f685bd5c68529b6f610/fonttools-4.57.0-py3-none-any.whl", hash = "sha256:3122c604a675513c68bd24c6a8f9091f1c2376d18e8f5fe5a101746c81b3e98f", size = 1093605, upload_time = "2025-04-03T11:07:11.341Z" }, + { url = "https://files.pythonhosted.org/packages/81/1f/e67c99aa3c6d3d2f93d956627e62a57ae0d35dc42f26611ea2a91053f6d6/fonttools-4.57.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3871349303bdec958360eedb619169a779956503ffb4543bb3e6211e09b647c4", size = 2757392, upload-time = "2025-04-03T11:05:45.715Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f1/f75770d0ddc67db504850898d96d75adde238c35313409bfcd8db4e4a5fe/fonttools-4.57.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c59375e85126b15a90fcba3443eaac58f3073ba091f02410eaa286da9ad80ed8", size = 2285609, upload-time = "2025-04-03T11:05:47.977Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d3/bc34e4953cb204bae0c50b527307dce559b810e624a733351a654cfc318e/fonttools-4.57.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967b65232e104f4b0f6370a62eb33089e00024f2ce143aecbf9755649421c683", size = 4873292, upload-time = "2025-04-03T11:05:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/41/b8/d5933559303a4ab18c799105f4c91ee0318cc95db4a2a09e300116625e7a/fonttools-4.57.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39acf68abdfc74e19de7485f8f7396fa4d2418efea239b7061d6ed6a2510c746", size = 4902503, upload-time = "2025-04-03T11:05:52.17Z" }, + { url = "https://files.pythonhosted.org/packages/32/13/acb36bfaa316f481153ce78de1fa3926a8bad42162caa3b049e1afe2408b/fonttools-4.57.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d077f909f2343daf4495ba22bb0e23b62886e8ec7c109ee8234bdbd678cf344", size = 5077351, upload-time = "2025-04-03T11:05:54.162Z" }, + { url = "https://files.pythonhosted.org/packages/b5/23/6d383a2ca83b7516d73975d8cca9d81a01acdcaa5e4db8579e4f3de78518/fonttools-4.57.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:46370ac47a1e91895d40e9ad48effbe8e9d9db1a4b80888095bc00e7beaa042f", size = 5275067, upload-time = "2025-04-03T11:05:57.375Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ca/31b8919c6da0198d5d522f1d26c980201378c087bdd733a359a1e7485769/fonttools-4.57.0-cp311-cp311-win32.whl", hash = "sha256:ca2aed95855506b7ae94e8f1f6217b7673c929e4f4f1217bcaa236253055cb36", size = 2158263, upload-time = "2025-04-03T11:05:59.567Z" }, + { url = "https://files.pythonhosted.org/packages/13/4c/de2612ea2216eb45cfc8eb91a8501615dd87716feaf5f8fb65cbca576289/fonttools-4.57.0-cp311-cp311-win_amd64.whl", hash = "sha256:17168a4670bbe3775f3f3f72d23ee786bd965395381dfbb70111e25e81505b9d", size = 2204968, upload-time = "2025-04-03T11:06:02.16Z" }, + { url = "https://files.pythonhosted.org/packages/cb/98/d4bc42d43392982eecaaca117d79845734d675219680cd43070bb001bc1f/fonttools-4.57.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:889e45e976c74abc7256d3064aa7c1295aa283c6bb19810b9f8b604dfe5c7f31", size = 2751824, upload-time = "2025-04-03T11:06:03.782Z" }, + { url = "https://files.pythonhosted.org/packages/1a/62/7168030eeca3742fecf45f31e63b5ef48969fa230a672216b805f1d61548/fonttools-4.57.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0425c2e052a5f1516c94e5855dbda706ae5a768631e9fcc34e57d074d1b65b92", size = 2283072, upload-time = "2025-04-03T11:06:05.533Z" }, + { url = "https://files.pythonhosted.org/packages/5d/82/121a26d9646f0986ddb35fbbaf58ef791c25b59ecb63ffea2aab0099044f/fonttools-4.57.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44c26a311be2ac130f40a96769264809d3b0cb297518669db437d1cc82974888", size = 4788020, upload-time = "2025-04-03T11:06:07.249Z" }, + { url = "https://files.pythonhosted.org/packages/5b/26/e0f2fb662e022d565bbe280a3cfe6dafdaabf58889ff86fdef2d31ff1dde/fonttools-4.57.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c41ba992df5b8d680b89fd84c6a1f2aca2b9f1ae8a67400c8930cd4ea115f6", size = 4859096, upload-time = "2025-04-03T11:06:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/9e/44/9075e323347b1891cdece4b3f10a3b84a8f4c42a7684077429d9ce842056/fonttools-4.57.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea1e9e43ca56b0c12440a7c689b1350066595bebcaa83baad05b8b2675129d98", size = 4964356, upload-time = "2025-04-03T11:06:11.294Z" }, + { url = "https://files.pythonhosted.org/packages/48/28/caa8df32743462fb966be6de6a79d7f30393859636d7732e82efa09fbbb4/fonttools-4.57.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84fd56c78d431606332a0627c16e2a63d243d0d8b05521257d77c6529abe14d8", size = 5226546, upload-time = "2025-04-03T11:06:13.6Z" }, + { url = "https://files.pythonhosted.org/packages/f6/46/95ab0f0d2e33c5b1a4fc1c0efe5e286ba9359602c0a9907adb1faca44175/fonttools-4.57.0-cp312-cp312-win32.whl", hash = "sha256:f4376819c1c778d59e0a31db5dc6ede854e9edf28bbfa5b756604727f7f800ac", size = 2146776, upload-time = "2025-04-03T11:06:15.643Z" }, + { url = "https://files.pythonhosted.org/packages/06/5d/1be5424bb305880e1113631f49a55ea7c7da3a5fe02608ca7c16a03a21da/fonttools-4.57.0-cp312-cp312-win_amd64.whl", hash = "sha256:57e30241524879ea10cdf79c737037221f77cc126a8cdc8ff2c94d4a522504b9", size = 2193956, upload-time = "2025-04-03T11:06:17.534Z" }, + { url = "https://files.pythonhosted.org/packages/90/27/45f8957c3132917f91aaa56b700bcfc2396be1253f685bd5c68529b6f610/fonttools-4.57.0-py3-none-any.whl", hash = "sha256:3122c604a675513c68bd24c6a8f9091f1c2376d18e8f5fe5a101746c81b3e98f", size = 1093605, upload-time = "2025-04-03T11:07:11.341Z" }, ] [[package]] name = "frozenlist" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831, upload_time = "2025-04-17T22:38:53.099Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f4/d744cba2da59b5c1d88823cf9e8a6c74e4659e2b27604ed973be2a0bf5ab/frozenlist-1.6.0.tar.gz", hash = "sha256:b99655c32c1c8e06d111e7f41c06c29a5318cb1835df23a45518e02a47c63b68", size = 42831, upload-time = "2025-04-17T22:38:53.099Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/b5/bc883b5296ec902115c00be161da93bf661199c465ec4c483feec6ea4c32/frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d", size = 160912, upload_time = "2025-04-17T22:36:17.235Z" }, - { url = "https://files.pythonhosted.org/packages/6f/93/51b058b563d0704b39c56baa222828043aafcac17fd3734bec5dbeb619b1/frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0", size = 124315, upload_time = "2025-04-17T22:36:18.735Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e0/46cd35219428d350558b874d595e132d1c17a9471a1bd0d01d518a261e7c/frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe", size = 122230, upload_time = "2025-04-17T22:36:20.6Z" }, - { url = "https://files.pythonhosted.org/packages/d1/0f/7ad2ce928ad06d6dd26a61812b959ded573d3e9d0ee6109d96c2be7172e9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba", size = 314842, upload_time = "2025-04-17T22:36:22.088Z" }, - { url = "https://files.pythonhosted.org/packages/34/76/98cbbd8a20a5c3359a2004ae5e5b216af84a150ccbad67c8f8f30fb2ea91/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595", size = 304919, upload_time = "2025-04-17T22:36:24.247Z" }, - { url = "https://files.pythonhosted.org/packages/9a/fa/258e771ce3a44348c05e6b01dffc2bc67603fba95761458c238cd09a2c77/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a", size = 324074, upload_time = "2025-04-17T22:36:26.291Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a4/047d861fd8c538210e12b208c0479912273f991356b6bdee7ea8356b07c9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626", size = 321292, upload_time = "2025-04-17T22:36:27.909Z" }, - { url = "https://files.pythonhosted.org/packages/c0/25/cfec8af758b4525676cabd36efcaf7102c1348a776c0d1ad046b8a7cdc65/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff", size = 301569, upload_time = "2025-04-17T22:36:29.448Z" }, - { url = "https://files.pythonhosted.org/packages/87/2f/0c819372fa9f0c07b153124bf58683b8d0ca7bb73ea5ccde9b9ef1745beb/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a", size = 313625, upload_time = "2025-04-17T22:36:31.55Z" }, - { url = "https://files.pythonhosted.org/packages/50/5f/f0cf8b0fdedffdb76b3745aa13d5dbe404d63493cc211ce8250f2025307f/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0", size = 312523, upload_time = "2025-04-17T22:36:33.078Z" }, - { url = "https://files.pythonhosted.org/packages/e1/6c/38c49108491272d3e84125bbabf2c2d0b304899b52f49f0539deb26ad18d/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606", size = 322657, upload_time = "2025-04-17T22:36:34.688Z" }, - { url = "https://files.pythonhosted.org/packages/bd/4b/3bd3bad5be06a9d1b04b1c22be80b5fe65b502992d62fab4bdb25d9366ee/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584", size = 303414, upload_time = "2025-04-17T22:36:36.363Z" }, - { url = "https://files.pythonhosted.org/packages/5b/89/7e225a30bef6e85dbfe22622c24afe932e9444de3b40d58b1ea589a14ef8/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a", size = 320321, upload_time = "2025-04-17T22:36:38.16Z" }, - { url = "https://files.pythonhosted.org/packages/22/72/7e3acef4dd9e86366cb8f4d8f28e852c2b7e116927e9722b31a6f71ea4b0/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1", size = 323975, upload_time = "2025-04-17T22:36:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/d8/85/e5da03d20507e13c66ce612c9792b76811b7a43e3320cce42d95b85ac755/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e", size = 316553, upload_time = "2025-04-17T22:36:42.045Z" }, - { url = "https://files.pythonhosted.org/packages/ac/8e/6c609cbd0580ae8a0661c408149f196aade7d325b1ae7adc930501b81acb/frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860", size = 115511, upload_time = "2025-04-17T22:36:44.067Z" }, - { url = "https://files.pythonhosted.org/packages/f2/13/a84804cfde6de12d44ed48ecbf777ba62b12ff09e761f76cdd1ff9e14bb1/frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603", size = 120863, upload_time = "2025-04-17T22:36:45.465Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8a/289b7d0de2fbac832ea80944d809759976f661557a38bb8e77db5d9f79b7/frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1", size = 160193, upload_time = "2025-04-17T22:36:47.382Z" }, - { url = "https://files.pythonhosted.org/packages/19/80/2fd17d322aec7f430549f0669f599997174f93ee17929ea5b92781ec902c/frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29", size = 123831, upload_time = "2025-04-17T22:36:49.401Z" }, - { url = "https://files.pythonhosted.org/packages/99/06/f5812da431273f78c6543e0b2f7de67dfd65eb0a433978b2c9c63d2205e4/frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25", size = 121862, upload_time = "2025-04-17T22:36:51.899Z" }, - { url = "https://files.pythonhosted.org/packages/d0/31/9e61c6b5fc493cf24d54881731204d27105234d09878be1a5983182cc4a5/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576", size = 316361, upload_time = "2025-04-17T22:36:53.402Z" }, - { url = "https://files.pythonhosted.org/packages/9d/55/22ca9362d4f0222324981470fd50192be200154d51509ee6eb9baa148e96/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8", size = 307115, upload_time = "2025-04-17T22:36:55.016Z" }, - { url = "https://files.pythonhosted.org/packages/ae/39/4fff42920a57794881e7bb3898dc7f5f539261711ea411b43bba3cde8b79/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9", size = 322505, upload_time = "2025-04-17T22:36:57.12Z" }, - { url = "https://files.pythonhosted.org/packages/55/f2/88c41f374c1e4cf0092a5459e5f3d6a1e17ed274c98087a76487783df90c/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e", size = 322666, upload_time = "2025-04-17T22:36:58.735Z" }, - { url = "https://files.pythonhosted.org/packages/75/51/034eeb75afdf3fd03997856195b500722c0b1a50716664cde64e28299c4b/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590", size = 302119, upload_time = "2025-04-17T22:37:00.512Z" }, - { url = "https://files.pythonhosted.org/packages/2b/a6/564ecde55ee633270a793999ef4fd1d2c2b32b5a7eec903b1012cb7c5143/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103", size = 316226, upload_time = "2025-04-17T22:37:02.102Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/6c0682c32377f402b8a6174fb16378b683cf6379ab4d2827c580892ab3c7/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c", size = 312788, upload_time = "2025-04-17T22:37:03.578Z" }, - { url = "https://files.pythonhosted.org/packages/b6/b8/10fbec38f82c5d163ca1750bfff4ede69713badf236a016781cf1f10a0f0/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821", size = 325914, upload_time = "2025-04-17T22:37:05.213Z" }, - { url = "https://files.pythonhosted.org/packages/62/ca/2bf4f3a1bd40cdedd301e6ecfdbb291080d5afc5f9ce350c0739f773d6b9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70", size = 305283, upload_time = "2025-04-17T22:37:06.985Z" }, - { url = "https://files.pythonhosted.org/packages/09/64/20cc13ccf94abc2a1f482f74ad210703dc78a590d0b805af1c9aa67f76f9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f", size = 319264, upload_time = "2025-04-17T22:37:08.618Z" }, - { url = "https://files.pythonhosted.org/packages/20/ff/86c6a2bbe98cfc231519f5e6d712a0898488ceac804a917ce014f32e68f6/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046", size = 326482, upload_time = "2025-04-17T22:37:10.196Z" }, - { url = "https://files.pythonhosted.org/packages/2f/da/8e381f66367d79adca245d1d71527aac774e30e291d41ef161ce2d80c38e/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770", size = 318248, upload_time = "2025-04-17T22:37:12.284Z" }, - { url = "https://files.pythonhosted.org/packages/39/24/1a1976563fb476ab6f0fa9fefaac7616a4361dbe0461324f9fd7bf425dbe/frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc", size = 115161, upload_time = "2025-04-17T22:37:13.902Z" }, - { url = "https://files.pythonhosted.org/packages/80/2e/fb4ed62a65f8cd66044706b1013f0010930d8cbb0729a2219561ea075434/frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878", size = 120548, upload_time = "2025-04-17T22:37:15.326Z" }, - { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404, upload_time = "2025-04-17T22:38:51.668Z" }, + { url = "https://files.pythonhosted.org/packages/53/b5/bc883b5296ec902115c00be161da93bf661199c465ec4c483feec6ea4c32/frozenlist-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae8337990e7a45683548ffb2fee1af2f1ed08169284cd829cdd9a7fa7470530d", size = 160912, upload-time = "2025-04-17T22:36:17.235Z" }, + { url = "https://files.pythonhosted.org/packages/6f/93/51b058b563d0704b39c56baa222828043aafcac17fd3734bec5dbeb619b1/frozenlist-1.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c952f69dd524558694818a461855f35d36cc7f5c0adddce37e962c85d06eac0", size = 124315, upload-time = "2025-04-17T22:36:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e0/46cd35219428d350558b874d595e132d1c17a9471a1bd0d01d518a261e7c/frozenlist-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f5fef13136c4e2dee91bfb9a44e236fff78fc2cd9f838eddfc470c3d7d90afe", size = 122230, upload-time = "2025-04-17T22:36:20.6Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0f/7ad2ce928ad06d6dd26a61812b959ded573d3e9d0ee6109d96c2be7172e9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:716bbba09611b4663ecbb7cd022f640759af8259e12a6ca939c0a6acd49eedba", size = 314842, upload-time = "2025-04-17T22:36:22.088Z" }, + { url = "https://files.pythonhosted.org/packages/34/76/98cbbd8a20a5c3359a2004ae5e5b216af84a150ccbad67c8f8f30fb2ea91/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7b8c4dc422c1a3ffc550b465090e53b0bf4839047f3e436a34172ac67c45d595", size = 304919, upload-time = "2025-04-17T22:36:24.247Z" }, + { url = "https://files.pythonhosted.org/packages/9a/fa/258e771ce3a44348c05e6b01dffc2bc67603fba95761458c238cd09a2c77/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b11534872256e1666116f6587a1592ef395a98b54476addb5e8d352925cb5d4a", size = 324074, upload-time = "2025-04-17T22:36:26.291Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a4/047d861fd8c538210e12b208c0479912273f991356b6bdee7ea8356b07c9/frozenlist-1.6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6eceb88aaf7221f75be6ab498dc622a151f5f88d536661af3ffc486245a626", size = 321292, upload-time = "2025-04-17T22:36:27.909Z" }, + { url = "https://files.pythonhosted.org/packages/c0/25/cfec8af758b4525676cabd36efcaf7102c1348a776c0d1ad046b8a7cdc65/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62c828a5b195570eb4b37369fcbbd58e96c905768d53a44d13044355647838ff", size = 301569, upload-time = "2025-04-17T22:36:29.448Z" }, + { url = "https://files.pythonhosted.org/packages/87/2f/0c819372fa9f0c07b153124bf58683b8d0ca7bb73ea5ccde9b9ef1745beb/frozenlist-1.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1c6bd2c6399920c9622362ce95a7d74e7f9af9bfec05fff91b8ce4b9647845a", size = 313625, upload-time = "2025-04-17T22:36:31.55Z" }, + { url = "https://files.pythonhosted.org/packages/50/5f/f0cf8b0fdedffdb76b3745aa13d5dbe404d63493cc211ce8250f2025307f/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49ba23817781e22fcbd45fd9ff2b9b8cdb7b16a42a4851ab8025cae7b22e96d0", size = 312523, upload-time = "2025-04-17T22:36:33.078Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6c/38c49108491272d3e84125bbabf2c2d0b304899b52f49f0539deb26ad18d/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:431ef6937ae0f853143e2ca67d6da76c083e8b1fe3df0e96f3802fd37626e606", size = 322657, upload-time = "2025-04-17T22:36:34.688Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4b/3bd3bad5be06a9d1b04b1c22be80b5fe65b502992d62fab4bdb25d9366ee/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9d124b38b3c299ca68433597ee26b7819209cb8a3a9ea761dfe9db3a04bba584", size = 303414, upload-time = "2025-04-17T22:36:36.363Z" }, + { url = "https://files.pythonhosted.org/packages/5b/89/7e225a30bef6e85dbfe22622c24afe932e9444de3b40d58b1ea589a14ef8/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:118e97556306402e2b010da1ef21ea70cb6d6122e580da64c056b96f524fbd6a", size = 320321, upload-time = "2025-04-17T22:36:38.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/72/7e3acef4dd9e86366cb8f4d8f28e852c2b7e116927e9722b31a6f71ea4b0/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb3b309f1d4086b5533cf7bbcf3f956f0ae6469664522f1bde4feed26fba60f1", size = 323975, upload-time = "2025-04-17T22:36:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/d8/85/e5da03d20507e13c66ce612c9792b76811b7a43e3320cce42d95b85ac755/frozenlist-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54dece0d21dce4fdb188a1ffc555926adf1d1c516e493c2914d7c370e454bc9e", size = 316553, upload-time = "2025-04-17T22:36:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/ac/8e/6c609cbd0580ae8a0661c408149f196aade7d325b1ae7adc930501b81acb/frozenlist-1.6.0-cp311-cp311-win32.whl", hash = "sha256:654e4ba1d0b2154ca2f096bed27461cf6160bc7f504a7f9a9ef447c293caf860", size = 115511, upload-time = "2025-04-17T22:36:44.067Z" }, + { url = "https://files.pythonhosted.org/packages/f2/13/a84804cfde6de12d44ed48ecbf777ba62b12ff09e761f76cdd1ff9e14bb1/frozenlist-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e911391bffdb806001002c1f860787542f45916c3baf764264a52765d5a5603", size = 120863, upload-time = "2025-04-17T22:36:45.465Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8a/289b7d0de2fbac832ea80944d809759976f661557a38bb8e77db5d9f79b7/frozenlist-1.6.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5b9e42ace7d95bf41e19b87cec8f262c41d3510d8ad7514ab3862ea2197bfb1", size = 160193, upload-time = "2025-04-17T22:36:47.382Z" }, + { url = "https://files.pythonhosted.org/packages/19/80/2fd17d322aec7f430549f0669f599997174f93ee17929ea5b92781ec902c/frozenlist-1.6.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ca9973735ce9f770d24d5484dcb42f68f135351c2fc81a7a9369e48cf2998a29", size = 123831, upload-time = "2025-04-17T22:36:49.401Z" }, + { url = "https://files.pythonhosted.org/packages/99/06/f5812da431273f78c6543e0b2f7de67dfd65eb0a433978b2c9c63d2205e4/frozenlist-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ac40ec76041c67b928ca8aaffba15c2b2ee3f5ae8d0cb0617b5e63ec119ca25", size = 121862, upload-time = "2025-04-17T22:36:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/d0/31/9e61c6b5fc493cf24d54881731204d27105234d09878be1a5983182cc4a5/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b7a8a3180dfb280eb044fdec562f9b461614c0ef21669aea6f1d3dac6ee576", size = 316361, upload-time = "2025-04-17T22:36:53.402Z" }, + { url = "https://files.pythonhosted.org/packages/9d/55/22ca9362d4f0222324981470fd50192be200154d51509ee6eb9baa148e96/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c444d824e22da6c9291886d80c7d00c444981a72686e2b59d38b285617cb52c8", size = 307115, upload-time = "2025-04-17T22:36:55.016Z" }, + { url = "https://files.pythonhosted.org/packages/ae/39/4fff42920a57794881e7bb3898dc7f5f539261711ea411b43bba3cde8b79/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb52c8166499a8150bfd38478248572c924c003cbb45fe3bcd348e5ac7c000f9", size = 322505, upload-time = "2025-04-17T22:36:57.12Z" }, + { url = "https://files.pythonhosted.org/packages/55/f2/88c41f374c1e4cf0092a5459e5f3d6a1e17ed274c98087a76487783df90c/frozenlist-1.6.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b35298b2db9c2468106278537ee529719228950a5fdda686582f68f247d1dc6e", size = 322666, upload-time = "2025-04-17T22:36:58.735Z" }, + { url = "https://files.pythonhosted.org/packages/75/51/034eeb75afdf3fd03997856195b500722c0b1a50716664cde64e28299c4b/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d108e2d070034f9d57210f22fefd22ea0d04609fc97c5f7f5a686b3471028590", size = 302119, upload-time = "2025-04-17T22:37:00.512Z" }, + { url = "https://files.pythonhosted.org/packages/2b/a6/564ecde55ee633270a793999ef4fd1d2c2b32b5a7eec903b1012cb7c5143/frozenlist-1.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e1be9111cb6756868ac242b3c2bd1f09d9aea09846e4f5c23715e7afb647103", size = 316226, upload-time = "2025-04-17T22:37:02.102Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/6c0682c32377f402b8a6174fb16378b683cf6379ab4d2827c580892ab3c7/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:94bb451c664415f02f07eef4ece976a2c65dcbab9c2f1705b7031a3a75349d8c", size = 312788, upload-time = "2025-04-17T22:37:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b8/10fbec38f82c5d163ca1750bfff4ede69713badf236a016781cf1f10a0f0/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d1a686d0b0949182b8faddea596f3fc11f44768d1f74d4cad70213b2e139d821", size = 325914, upload-time = "2025-04-17T22:37:05.213Z" }, + { url = "https://files.pythonhosted.org/packages/62/ca/2bf4f3a1bd40cdedd301e6ecfdbb291080d5afc5f9ce350c0739f773d6b9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ea8e59105d802c5a38bdbe7362822c522230b3faba2aa35c0fa1765239b7dd70", size = 305283, upload-time = "2025-04-17T22:37:06.985Z" }, + { url = "https://files.pythonhosted.org/packages/09/64/20cc13ccf94abc2a1f482f74ad210703dc78a590d0b805af1c9aa67f76f9/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:abc4e880a9b920bc5020bf6a431a6bb40589d9bca3975c980495f63632e8382f", size = 319264, upload-time = "2025-04-17T22:37:08.618Z" }, + { url = "https://files.pythonhosted.org/packages/20/ff/86c6a2bbe98cfc231519f5e6d712a0898488ceac804a917ce014f32e68f6/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a79713adfe28830f27a3c62f6b5406c37376c892b05ae070906f07ae4487046", size = 326482, upload-time = "2025-04-17T22:37:10.196Z" }, + { url = "https://files.pythonhosted.org/packages/2f/da/8e381f66367d79adca245d1d71527aac774e30e291d41ef161ce2d80c38e/frozenlist-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a0318c2068e217a8f5e3b85e35899f5a19e97141a45bb925bb357cfe1daf770", size = 318248, upload-time = "2025-04-17T22:37:12.284Z" }, + { url = "https://files.pythonhosted.org/packages/39/24/1a1976563fb476ab6f0fa9fefaac7616a4361dbe0461324f9fd7bf425dbe/frozenlist-1.6.0-cp312-cp312-win32.whl", hash = "sha256:853ac025092a24bb3bf09ae87f9127de9fe6e0c345614ac92536577cf956dfcc", size = 115161, upload-time = "2025-04-17T22:37:13.902Z" }, + { url = "https://files.pythonhosted.org/packages/80/2e/fb4ed62a65f8cd66044706b1013f0010930d8cbb0729a2219561ea075434/frozenlist-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bdfe2d7e6c9281c6e55523acd6c2bf77963cb422fdc7d142fb0cb6621b66878", size = 120548, upload-time = "2025-04-17T22:37:15.326Z" }, + { url = "https://files.pythonhosted.org/packages/71/3e/b04a0adda73bd52b390d730071c0d577073d3d26740ee1bad25c3ad0f37b/frozenlist-1.6.0-py3-none-any.whl", hash = "sha256:535eec9987adb04701266b92745d6cdcef2e77669299359c3009c3404dd5d191", size = 12404, upload-time = "2025-04-17T22:38:51.668Z" }, ] [[package]] name = "future-fstrings" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5d/e2/3874574cce18a2e3608abfe5b4b5b3c9765653c464f5da18df8971cf501d/future_fstrings-1.2.0.tar.gz", hash = "sha256:6cf41cbe97c398ab5a81168ce0dbb8ad95862d3caf23c21e4430627b90844089", size = 5786, upload_time = "2019-06-16T03:04:42.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/e2/3874574cce18a2e3608abfe5b4b5b3c9765653c464f5da18df8971cf501d/future_fstrings-1.2.0.tar.gz", hash = "sha256:6cf41cbe97c398ab5a81168ce0dbb8ad95862d3caf23c21e4430627b90844089", size = 5786, upload-time = "2019-06-16T03:04:42.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/6d/ea1d52e9038558dd37f5d30647eb9f07888c164960a5d4daa5f970c6da25/future_fstrings-1.2.0-py2.py3-none-any.whl", hash = "sha256:90e49598b553d8746c4dc7d9442e0359d038c3039d802c91c0a55505da318c63", size = 6138, upload_time = "2019-06-16T03:04:40.395Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6d/ea1d52e9038558dd37f5d30647eb9f07888c164960a5d4daa5f970c6da25/future_fstrings-1.2.0-py2.py3-none-any.whl", hash = "sha256:90e49598b553d8746c4dc7d9442e0359d038c3039d802c91c0a55505da318c63", size = 6138, upload-time = "2019-06-16T03:04:40.395Z" }, ] [[package]] @@ -620,29 +620,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload_time = "2022-05-02T15:47:16.11Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload_time = "2022-05-02T15:47:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, ] [[package]] name = "google-crc32c" version = "1.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload_time = "2025-03-26T14:29:13.32Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/ae/87802e6d9f9d69adfaedfcfd599266bf386a54d0be058b532d04c794f76d/google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472", size = 14495, upload-time = "2025-03-26T14:29:13.32Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468, upload_time = "2025-03-26T14:32:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313, upload_time = "2025-03-26T14:57:38.758Z" }, - { url = "https://files.pythonhosted.org/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048, upload_time = "2025-03-26T14:41:30.679Z" }, - { url = "https://files.pythonhosted.org/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669, upload_time = "2025-03-26T14:41:31.432Z" }, - { url = "https://files.pythonhosted.org/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476, upload_time = "2025-03-26T14:29:10.211Z" }, - { url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470, upload_time = "2025-03-26T14:34:31.655Z" }, - { url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315, upload_time = "2025-03-26T15:01:54.634Z" }, - { url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180, upload_time = "2025-03-26T14:41:32.168Z" }, - { url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794, upload_time = "2025-03-26T14:41:33.264Z" }, - { url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477, upload_time = "2025-03-26T14:29:10.94Z" }, - { url = "https://files.pythonhosted.org/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241, upload_time = "2025-03-26T14:41:45.898Z" }, - { url = "https://files.pythonhosted.org/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048, upload_time = "2025-03-26T14:41:46.696Z" }, + { url = "https://files.pythonhosted.org/packages/f7/94/220139ea87822b6fdfdab4fb9ba81b3fff7ea2c82e2af34adc726085bffc/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06", size = 30468, upload-time = "2025-03-26T14:32:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/94/97/789b23bdeeb9d15dc2904660463ad539d0318286d7633fe2760c10ed0c1c/google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9", size = 30313, upload-time = "2025-03-26T14:57:38.758Z" }, + { url = "https://files.pythonhosted.org/packages/81/b8/976a2b843610c211e7ccb3e248996a61e87dbb2c09b1499847e295080aec/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77", size = 33048, upload-time = "2025-03-26T14:41:30.679Z" }, + { url = "https://files.pythonhosted.org/packages/c9/16/a3842c2cf591093b111d4a5e2bfb478ac6692d02f1b386d2a33283a19dc9/google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53", size = 32669, upload-time = "2025-03-26T14:41:31.432Z" }, + { url = "https://files.pythonhosted.org/packages/04/17/ed9aba495916fcf5fe4ecb2267ceb851fc5f273c4e4625ae453350cfd564/google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d", size = 33476, upload-time = "2025-03-26T14:29:10.211Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b7/787e2453cf8639c94b3d06c9d61f512234a82e1d12d13d18584bd3049904/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194", size = 30470, upload-time = "2025-03-26T14:34:31.655Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b4/6042c2b0cbac3ec3a69bb4c49b28d2f517b7a0f4a0232603c42c58e22b44/google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e", size = 30315, upload-time = "2025-03-26T15:01:54.634Z" }, + { url = "https://files.pythonhosted.org/packages/29/ad/01e7a61a5d059bc57b702d9ff6a18b2585ad97f720bd0a0dbe215df1ab0e/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337", size = 33180, upload-time = "2025-03-26T14:41:32.168Z" }, + { url = "https://files.pythonhosted.org/packages/3b/a5/7279055cf004561894ed3a7bfdf5bf90a53f28fadd01af7cd166e88ddf16/google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65", size = 32794, upload-time = "2025-03-26T14:41:33.264Z" }, + { url = "https://files.pythonhosted.org/packages/0f/d6/77060dbd140c624e42ae3ece3df53b9d811000729a5c821b9fd671ceaac6/google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6", size = 33477, upload-time = "2025-03-26T14:29:10.94Z" }, + { url = "https://files.pythonhosted.org/packages/16/1b/1693372bf423ada422f80fd88260dbfd140754adb15cbc4d7e9a68b1cb8e/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48", size = 28241, upload-time = "2025-03-26T14:41:45.898Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3c/2a19a60a473de48717b4efb19398c3f914795b64a96cf3fbe82588044f78/google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82", size = 28048, upload-time = "2025-03-26T14:41:46.696Z" }, ] [[package]] @@ -655,9 +655,9 @@ dependencies = [ { name = "numpy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326, upload_time = "2025-03-06T16:30:36.428Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326, upload-time = "2025-03-06T16:30:36.428Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/68/2bdc7b46b5f543dd865575f9d19716866bdb76e50dd33b71ed1a3dd8bb42/gymnasium-1.1.1-py3-none-any.whl", hash = "sha256:9c167ec0a2b388666e37f63b2849cd2552f7f5b71938574c637bb36487eb928a", size = 965410, upload_time = "2025-03-06T16:30:34.443Z" }, + { url = "https://files.pythonhosted.org/packages/f9/68/2bdc7b46b5f543dd865575f9d19716866bdb76e50dd33b71ed1a3dd8bb42/gymnasium-1.1.1-py3-none-any.whl", hash = "sha256:9c167ec0a2b388666e37f63b2849cd2552f7f5b71938574c637bb36487eb928a", size = 965410, upload-time = "2025-03-06T16:30:34.443Z" }, ] [[package]] @@ -668,54 +668,54 @@ dependencies = [ { name = "attrs" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/f2/f77da8271b1abb630cb2090ead2f5aa4acc9639d632e8e68187f52527e4b/hypothesis-6.47.5.tar.gz", hash = "sha256:e0c1e253fc97e7ecdb9e2bbff2cf815d8739e0d1d3d093d67c3af5bb6a7211b0", size = 326641, upload_time = "2022-06-25T20:58:48.926Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/f2/f77da8271b1abb630cb2090ead2f5aa4acc9639d632e8e68187f52527e4b/hypothesis-6.47.5.tar.gz", hash = "sha256:e0c1e253fc97e7ecdb9e2bbff2cf815d8739e0d1d3d093d67c3af5bb6a7211b0", size = 326641, upload-time = "2022-06-25T20:58:48.926Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/a7/389bbaade2cbbb2534cb2715986041ed01c6d792152c527e71f7f68e93b5/hypothesis-6.47.5-py3-none-any.whl", hash = "sha256:87049b781ee11ec1c7948565b889ab02e428a1e32d427ab4de8fdb3649242d06", size = 387311, upload_time = "2022-06-25T20:58:45.281Z" }, + { url = "https://files.pythonhosted.org/packages/d3/a7/389bbaade2cbbb2534cb2715986041ed01c6d792152c527e71f7f68e93b5/hypothesis-6.47.5-py3-none-any.whl", hash = "sha256:87049b781ee11ec1c7948565b889ab02e428a1e32d427ab4de8fdb3649242d06", size = 387311, upload-time = "2022-06-25T20:58:45.281Z" }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload_time = "2024-09-15T18:07:39.745Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] [[package]] name = "ifaddr" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/fb4c578f4a3256561548cd825646680edcadb9440f3f68add95ade1eb791/ifaddr-0.2.0.tar.gz", hash = "sha256:cc0cbfcaabf765d44595825fb96a99bb12c79716b73b44330ea38ee2b0c4aed4", size = 10485, upload_time = "2022-06-15T21:40:27.561Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/fb4c578f4a3256561548cd825646680edcadb9440f3f68add95ade1eb791/ifaddr-0.2.0.tar.gz", hash = "sha256:cc0cbfcaabf765d44595825fb96a99bb12c79716b73b44330ea38ee2b0c4aed4", size = 10485, upload-time = "2022-06-15T21:40:27.561Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/1f/19ebc343cc71a7ffa78f17018535adc5cbdd87afb31d7c34874680148b32/ifaddr-0.2.0-py3-none-any.whl", hash = "sha256:085e0305cfe6f16ab12d72e2024030f5d52674afad6911bb1eee207177b8a748", size = 12314, upload_time = "2022-06-15T21:40:25.756Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1f/19ebc343cc71a7ffa78f17018535adc5cbdd87afb31d7c34874680148b32/ifaddr-0.2.0-py3-none-any.whl", hash = "sha256:085e0305cfe6f16ab12d72e2024030f5d52674afad6911bb1eee207177b8a748", size = 12314, upload-time = "2022-06-15T21:40:25.756Z" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload_time = "2025-03-19T20:09:59.721Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload_time = "2025-03-19T20:10:01.071Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "inputs" version = "0.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/cd/5f434220920f76eb73d19bb7aab8d857445f40aa642718e6e51e850cd663/inputs-0.5.tar.gz", hash = "sha256:a31d5b96a3525f1232f326be9e7ce8ccaf873c6b1fb84d9f3c9bc3d79b23eae4", size = 33393, upload_time = "2018-10-05T22:38:14.206Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/cd/5f434220920f76eb73d19bb7aab8d857445f40aa642718e6e51e850cd663/inputs-0.5.tar.gz", hash = "sha256:a31d5b96a3525f1232f326be9e7ce8ccaf873c6b1fb84d9f3c9bc3d79b23eae4", size = 33393, upload-time = "2018-10-05T22:38:14.206Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/94/040a0d9c81f018c39bd887b7b825013b024deb0a6c795f9524797e2cd41b/inputs-0.5-py2.py3-none-any.whl", hash = "sha256:13f894564e52134cf1e3862b1811da034875eb1f2b62e6021e3776e9669a96ec", size = 33630, upload_time = "2018-10-05T22:38:28.28Z" }, + { url = "https://files.pythonhosted.org/packages/d0/94/040a0d9c81f018c39bd887b7b825013b024deb0a6c795f9524797e2cd41b/inputs-0.5-py2.py3-none-any.whl", hash = "sha256:13f894564e52134cf1e3862b1811da034875eb1f2b62e6021e3776e9669a96ec", size = 33630, upload-time = "2018-10-05T22:38:28.28Z" }, ] [[package]] name = "isodate" version = "0.7.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload_time = "2024-10-08T23:04:11.5Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload_time = "2024-10-08T23:04:09.501Z" }, + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, ] [[package]] @@ -725,198 +725,198 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload_time = "2025-03-05T20:05:02.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload_time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] name = "json-rpc" version = "1.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/9e/59f4a5b7855ced7346ebf40a2e9a8942863f644378d956f68bcef2c88b90/json-rpc-1.15.0.tar.gz", hash = "sha256:e6441d56c1dcd54241c937d0a2dcd193bdf0bdc539b5316524713f554b7f85b9", size = 28854, upload_time = "2023-06-11T09:45:49.078Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/9e/59f4a5b7855ced7346ebf40a2e9a8942863f644378d956f68bcef2c88b90/json-rpc-1.15.0.tar.gz", hash = "sha256:e6441d56c1dcd54241c937d0a2dcd193bdf0bdc539b5316524713f554b7f85b9", size = 28854, upload-time = "2023-06-11T09:45:49.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/9e/820c4b086ad01ba7d77369fb8b11470a01fac9b4977f02e18659cf378b6b/json_rpc-1.15.0-py2.py3-none-any.whl", hash = "sha256:4a4668bbbe7116feb4abbd0f54e64a4adcf4b8f648f19ffa0848ad0f6606a9bf", size = 39450, upload_time = "2023-06-11T09:45:47.136Z" }, + { url = "https://files.pythonhosted.org/packages/94/9e/820c4b086ad01ba7d77369fb8b11470a01fac9b4977f02e18659cf378b6b/json_rpc-1.15.0-py2.py3-none-any.whl", hash = "sha256:4a4668bbbe7116feb4abbd0f54e64a4adcf4b8f648f19ffa0848ad0f6606a9bf", size = 39450, upload-time = "2023-06-11T09:45:47.136Z" }, ] [[package]] name = "kiwisolver" version = "1.4.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538, upload_time = "2024-12-24T18:30:51.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/59/7c91426a8ac292e1cdd53a63b6d9439abd573c875c3f92c146767dd33faf/kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e", size = 97538, upload-time = "2024-12-24T18:30:51.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635, upload_time = "2024-12-24T18:28:51.826Z" }, - { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717, upload_time = "2024-12-24T18:28:54.256Z" }, - { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413, upload_time = "2024-12-24T18:28:55.184Z" }, - { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994, upload_time = "2024-12-24T18:28:57.493Z" }, - { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804, upload_time = "2024-12-24T18:29:00.077Z" }, - { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690, upload_time = "2024-12-24T18:29:01.401Z" }, - { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839, upload_time = "2024-12-24T18:29:02.685Z" }, - { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109, upload_time = "2024-12-24T18:29:04.113Z" }, - { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269, upload_time = "2024-12-24T18:29:05.488Z" }, - { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468, upload_time = "2024-12-24T18:29:06.79Z" }, - { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394, upload_time = "2024-12-24T18:29:08.24Z" }, - { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901, upload_time = "2024-12-24T18:29:09.653Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306, upload_time = "2024-12-24T18:29:12.644Z" }, - { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966, upload_time = "2024-12-24T18:29:14.089Z" }, - { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311, upload_time = "2024-12-24T18:29:15.892Z" }, - { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152, upload_time = "2024-12-24T18:29:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555, upload_time = "2024-12-24T18:29:19.146Z" }, - { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067, upload_time = "2024-12-24T18:29:20.096Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443, upload_time = "2024-12-24T18:29:22.843Z" }, - { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728, upload_time = "2024-12-24T18:29:24.463Z" }, - { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388, upload_time = "2024-12-24T18:29:25.776Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849, upload_time = "2024-12-24T18:29:27.202Z" }, - { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533, upload_time = "2024-12-24T18:29:28.638Z" }, - { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898, upload_time = "2024-12-24T18:29:30.368Z" }, - { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605, upload_time = "2024-12-24T18:29:33.151Z" }, - { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801, upload_time = "2024-12-24T18:29:34.584Z" }, - { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077, upload_time = "2024-12-24T18:29:36.138Z" }, - { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload_time = "2024-12-24T18:29:39.991Z" }, - { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload_time = "2024-12-24T18:29:42.006Z" }, - { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload_time = "2024-12-24T18:29:44.38Z" }, + { url = "https://files.pythonhosted.org/packages/da/ed/c913ee28936c371418cb167b128066ffb20bbf37771eecc2c97edf8a6e4c/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84", size = 124635, upload-time = "2024-12-24T18:28:51.826Z" }, + { url = "https://files.pythonhosted.org/packages/4c/45/4a7f896f7467aaf5f56ef093d1f329346f3b594e77c6a3c327b2d415f521/kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561", size = 66717, upload-time = "2024-12-24T18:28:54.256Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b4/c12b3ac0852a3a68f94598d4c8d569f55361beef6159dce4e7b624160da2/kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7", size = 65413, upload-time = "2024-12-24T18:28:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/a9/98/1df4089b1ed23d83d410adfdc5947245c753bddfbe06541c4aae330e9e70/kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03", size = 1343994, upload-time = "2024-12-24T18:28:57.493Z" }, + { url = "https://files.pythonhosted.org/packages/8d/bf/b4b169b050c8421a7c53ea1ea74e4ef9c335ee9013216c558a047f162d20/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954", size = 1434804, upload-time = "2024-12-24T18:29:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/66/5a/e13bd341fbcf73325ea60fdc8af752addf75c5079867af2e04cc41f34434/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79", size = 1450690, upload-time = "2024-12-24T18:29:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4f/5955dcb376ba4a830384cc6fab7d7547bd6759fe75a09564910e9e3bb8ea/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6", size = 1376839, upload-time = "2024-12-24T18:29:02.685Z" }, + { url = "https://files.pythonhosted.org/packages/3a/97/5edbed69a9d0caa2e4aa616ae7df8127e10f6586940aa683a496c2c280b9/kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0", size = 1435109, upload-time = "2024-12-24T18:29:04.113Z" }, + { url = "https://files.pythonhosted.org/packages/13/fc/e756382cb64e556af6c1809a1bbb22c141bbc2445049f2da06b420fe52bf/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab", size = 2245269, upload-time = "2024-12-24T18:29:05.488Z" }, + { url = "https://files.pythonhosted.org/packages/76/15/e59e45829d7f41c776d138245cabae6515cb4eb44b418f6d4109c478b481/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc", size = 2393468, upload-time = "2024-12-24T18:29:06.79Z" }, + { url = "https://files.pythonhosted.org/packages/e9/39/483558c2a913ab8384d6e4b66a932406f87c95a6080112433da5ed668559/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25", size = 2355394, upload-time = "2024-12-24T18:29:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/efad1fbca6570a161d29224f14b082960c7e08268a133fe5dc0f6906820e/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc", size = 2490901, upload-time = "2024-12-24T18:29:09.653Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4f/15988966ba46bcd5ab9d0c8296914436720dd67fca689ae1a75b4ec1c72f/kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67", size = 2312306, upload-time = "2024-12-24T18:29:12.644Z" }, + { url = "https://files.pythonhosted.org/packages/2d/27/bdf1c769c83f74d98cbc34483a972f221440703054894a37d174fba8aa68/kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34", size = 71966, upload-time = "2024-12-24T18:29:14.089Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c9/9642ea855604aeb2968a8e145fc662edf61db7632ad2e4fb92424be6b6c0/kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2", size = 65311, upload-time = "2024-12-24T18:29:15.892Z" }, + { url = "https://files.pythonhosted.org/packages/fc/aa/cea685c4ab647f349c3bc92d2daf7ae34c8e8cf405a6dcd3a497f58a2ac3/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502", size = 124152, upload-time = "2024-12-24T18:29:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0b/8db6d2e2452d60d5ebc4ce4b204feeb16176a851fd42462f66ade6808084/kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31", size = 66555, upload-time = "2024-12-24T18:29:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/60/26/d6a0db6785dd35d3ba5bf2b2df0aedc5af089962c6eb2cbf67a15b81369e/kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb", size = 65067, upload-time = "2024-12-24T18:29:20.096Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ed/1d97f7e3561e09757a196231edccc1bcf59d55ddccefa2afc9c615abd8e0/kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f", size = 1378443, upload-time = "2024-12-24T18:29:22.843Z" }, + { url = "https://files.pythonhosted.org/packages/29/61/39d30b99954e6b46f760e6289c12fede2ab96a254c443639052d1b573fbc/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc", size = 1472728, upload-time = "2024-12-24T18:29:24.463Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3e/804163b932f7603ef256e4a715e5843a9600802bb23a68b4e08c8c0ff61d/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a", size = 1478388, upload-time = "2024-12-24T18:29:25.776Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/60eaa75169a154700be74f875a4d9961b11ba048bef315fbe89cb6999056/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a", size = 1413849, upload-time = "2024-12-24T18:29:27.202Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b3/9458adb9472e61a998c8c4d95cfdfec91c73c53a375b30b1428310f923e4/kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a", size = 1475533, upload-time = "2024-12-24T18:29:28.638Z" }, + { url = "https://files.pythonhosted.org/packages/e4/7a/0a42d9571e35798de80aef4bb43a9b672aa7f8e58643d7bd1950398ffb0a/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3", size = 2268898, upload-time = "2024-12-24T18:29:30.368Z" }, + { url = "https://files.pythonhosted.org/packages/d9/07/1255dc8d80271400126ed8db35a1795b1a2c098ac3a72645075d06fe5c5d/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b", size = 2425605, upload-time = "2024-12-24T18:29:33.151Z" }, + { url = "https://files.pythonhosted.org/packages/84/df/5a3b4cf13780ef6f6942df67b138b03b7e79e9f1f08f57c49957d5867f6e/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4", size = 2375801, upload-time = "2024-12-24T18:29:34.584Z" }, + { url = "https://files.pythonhosted.org/packages/8f/10/2348d068e8b0f635c8c86892788dac7a6b5c0cb12356620ab575775aad89/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d", size = 2520077, upload-time = "2024-12-24T18:29:36.138Z" }, + { url = "https://files.pythonhosted.org/packages/32/d8/014b89fee5d4dce157d814303b0fce4d31385a2af4c41fed194b173b81ac/kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8", size = 2338410, upload-time = "2024-12-24T18:29:39.991Z" }, + { url = "https://files.pythonhosted.org/packages/bd/72/dfff0cc97f2a0776e1c9eb5bef1ddfd45f46246c6533b0191887a427bca5/kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50", size = 71853, upload-time = "2024-12-24T18:29:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/dc/85/220d13d914485c0948a00f0b9eb419efaf6da81b7d72e88ce2391f7aed8d/kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476", size = 65424, upload-time = "2024-12-24T18:29:44.38Z" }, ] [[package]] name = "libusb1" version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/7f/c59ad56d1bca8fa4321d1bb77ba4687775751a4deceec14943a44da18ca0/libusb1-3.3.1.tar.gz", hash = "sha256:3951d360f2daf0e0eacf839e15d2d1d2f4f5e7830231eb3188eeffef2dd17bad", size = 107600, upload_time = "2025-03-24T05:36:47.834Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/7f/c59ad56d1bca8fa4321d1bb77ba4687775751a4deceec14943a44da18ca0/libusb1-3.3.1.tar.gz", hash = "sha256:3951d360f2daf0e0eacf839e15d2d1d2f4f5e7830231eb3188eeffef2dd17bad", size = 107600, upload-time = "2025-03-24T05:36:47.834Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/f7/4577cfc55c9520ecab5563173e83235382ac7980c8c2c08d6c9f7ef9e615/libusb1-3.3.1-py3-none-any.whl", hash = "sha256:808c9362299dcee01651aa87e71e9d681ccedb27fc4dbd70aaf14e245fb855f1", size = 67243, upload_time = "2025-03-24T05:36:42.312Z" }, - { url = "https://files.pythonhosted.org/packages/6a/60/d3fd4831c601f063a16fc59f465ef4c1108247b07fbff371a982bd1bac45/libusb1-3.3.1-py3-none-win32.whl", hash = "sha256:0ef69825173ce74af34444754c081cc324233edc6acc405658b3ad784833e076", size = 129576, upload_time = "2025-03-24T05:36:45.202Z" }, - { url = "https://files.pythonhosted.org/packages/94/6d/344a164d32d65d503ffe9201cd74cf13a020099dc446554d1e50b07f167b/libusb1-3.3.1-py3-none-win_amd64.whl", hash = "sha256:6e21b772d80d6487fbb55d3d2141218536db302da82f1983754e96c72781c102", size = 141080, upload_time = "2025-03-24T05:36:46.594Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/4577cfc55c9520ecab5563173e83235382ac7980c8c2c08d6c9f7ef9e615/libusb1-3.3.1-py3-none-any.whl", hash = "sha256:808c9362299dcee01651aa87e71e9d681ccedb27fc4dbd70aaf14e245fb855f1", size = 67243, upload-time = "2025-03-24T05:36:42.312Z" }, + { url = "https://files.pythonhosted.org/packages/6a/60/d3fd4831c601f063a16fc59f465ef4c1108247b07fbff371a982bd1bac45/libusb1-3.3.1-py3-none-win32.whl", hash = "sha256:0ef69825173ce74af34444754c081cc324233edc6acc405658b3ad784833e076", size = 129576, upload-time = "2025-03-24T05:36:45.202Z" }, + { url = "https://files.pythonhosted.org/packages/94/6d/344a164d32d65d503ffe9201cd74cf13a020099dc446554d1e50b07f167b/libusb1-3.3.1-py3-none-win_amd64.whl", hash = "sha256:6e21b772d80d6487fbb55d3d2141218536db302da82f1983754e96c72781c102", size = 141080, upload-time = "2025-03-24T05:36:46.594Z" }, ] [[package]] name = "llvmlite" version = "0.44.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880, upload_time = "2025-01-20T11:14:41.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/6a/95a3d3610d5c75293d5dbbb2a76480d5d4eeba641557b69fe90af6c5b84e/llvmlite-0.44.0.tar.gz", hash = "sha256:07667d66a5d150abed9157ab6c0b9393c9356f229784a4385c02f99e94fc94d4", size = 171880, upload-time = "2025-01-20T11:14:41.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", size = 28132305, upload_time = "2025-01-20T11:12:53.936Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", size = 26201090, upload_time = "2025-01-20T11:12:59.847Z" }, - { url = "https://files.pythonhosted.org/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", size = 42361858, upload_time = "2025-01-20T11:13:07.623Z" }, - { url = "https://files.pythonhosted.org/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610", size = 41184200, upload_time = "2025-01-20T11:13:20.058Z" }, - { url = "https://files.pythonhosted.org/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", size = 30331193, upload_time = "2025-01-20T11:13:26.976Z" }, - { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297, upload_time = "2025-01-20T11:13:32.57Z" }, - { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105, upload_time = "2025-01-20T11:13:38.744Z" }, - { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901, upload_time = "2025-01-20T11:13:46.711Z" }, - { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247, upload_time = "2025-01-20T11:13:56.159Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380, upload_time = "2025-01-20T11:14:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e2/86b245397052386595ad726f9742e5223d7aea999b18c518a50e96c3aca4/llvmlite-0.44.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:eed7d5f29136bda63b6d7804c279e2b72e08c952b7c5df61f45db408e0ee52f3", size = 28132305, upload-time = "2025-01-20T11:12:53.936Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ec/506902dc6870249fbe2466d9cf66d531265d0f3a1157213c8f986250c033/llvmlite-0.44.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ace564d9fa44bb91eb6e6d8e7754977783c68e90a471ea7ce913bff30bd62427", size = 26201090, upload-time = "2025-01-20T11:12:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/99/fe/d030f1849ebb1f394bb3f7adad5e729b634fb100515594aca25c354ffc62/llvmlite-0.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5d22c3bfc842668168a786af4205ec8e3ad29fb1bc03fd11fd48460d0df64c1", size = 42361858, upload-time = "2025-01-20T11:13:07.623Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7a/ce6174664b9077fc673d172e4c888cb0b128e707e306bc33fff8c2035f0d/llvmlite-0.44.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f01a394e9c9b7b1d4e63c327b096d10f6f0ed149ef53d38a09b3749dcf8c9610", size = 41184200, upload-time = "2025-01-20T11:13:20.058Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c6/258801143975a6d09a373f2641237992496e15567b907a4d401839d671b8/llvmlite-0.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8489634d43c20cd0ad71330dde1d5bc7b9966937a263ff1ec1cebb90dc50955", size = 30331193, upload-time = "2025-01-20T11:13:26.976Z" }, + { url = "https://files.pythonhosted.org/packages/15/86/e3c3195b92e6e492458f16d233e58a1a812aa2bfbef9bdd0fbafcec85c60/llvmlite-0.44.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:1d671a56acf725bf1b531d5ef76b86660a5ab8ef19bb6a46064a705c6ca80aad", size = 28132297, upload-time = "2025-01-20T11:13:32.57Z" }, + { url = "https://files.pythonhosted.org/packages/d6/53/373b6b8be67b9221d12b24125fd0ec56b1078b660eeae266ec388a6ac9a0/llvmlite-0.44.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f79a728e0435493611c9f405168682bb75ffd1fbe6fc360733b850c80a026db", size = 26201105, upload-time = "2025-01-20T11:13:38.744Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/8341fd3056419441286c8e26bf436923021005ece0bff5f41906476ae514/llvmlite-0.44.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0143a5ef336da14deaa8ec26c5449ad5b6a2b564df82fcef4be040b9cacfea9", size = 42361901, upload-time = "2025-01-20T11:13:46.711Z" }, + { url = "https://files.pythonhosted.org/packages/53/ad/d79349dc07b8a395a99153d7ce8b01d6fcdc9f8231355a5df55ded649b61/llvmlite-0.44.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d752f89e31b66db6f8da06df8b39f9b91e78c5feea1bf9e8c1fba1d1c24c065d", size = 41184247, upload-time = "2025-01-20T11:13:56.159Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3b/a9a17366af80127bd09decbe2a54d8974b6d8b274b39bf47fbaedeec6307/llvmlite-0.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:eae7e2d4ca8f88f89d315b48c6b741dcb925d6a1042da694aa16ab3dd4cbd3a1", size = 30332380, upload-time = "2025-01-20T11:14:02.442Z" }, ] [[package]] name = "lru-dict" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/e3/42c87871920602a3c8300915bd0292f76eccc66c38f782397acbf8a62088/lru-dict-1.3.0.tar.gz", hash = "sha256:54fd1966d6bd1fcde781596cb86068214edeebff1db13a2cea11079e3fd07b6b", size = 13123, upload_time = "2023-11-06T01:40:12.951Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/e3/42c87871920602a3c8300915bd0292f76eccc66c38f782397acbf8a62088/lru-dict-1.3.0.tar.gz", hash = "sha256:54fd1966d6bd1fcde781596cb86068214edeebff1db13a2cea11079e3fd07b6b", size = 13123, upload-time = "2023-11-06T01:40:12.951Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/c9/6fac0cb67160f0efa3cc76a6a7d04d5e21a516eeb991ebba08f4f8f01ec5/lru_dict-1.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:20c595764695d20bdc3ab9b582e0cc99814da183544afb83783a36d6741a0dac", size = 17750, upload_time = "2023-11-06T01:38:52.667Z" }, - { url = "https://files.pythonhosted.org/packages/61/14/f90dee4bc547ae266dbeffd4e11611234bb6af511dea48f3bc8dac1de478/lru_dict-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d9b30a8f50c3fa72a494eca6be5810a1b5c89e4f0fda89374f0d1c5ad8d37d51", size = 11055, upload_time = "2023-11-06T01:38:53.798Z" }, - { url = "https://files.pythonhosted.org/packages/4e/63/a0ae20525f9d52f62ac0def47935f8a2b3b6fcd2c145218b9a27fc1fb910/lru_dict-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9710737584650a4251b9a566cbb1a86f83437adb209c9ba43a4e756d12faf0d7", size = 11330, upload_time = "2023-11-06T01:38:54.847Z" }, - { url = "https://files.pythonhosted.org/packages/e9/c6/8c2b81b61e5206910c81b712500736227289aefe4ccfb36137aa21807003/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b84c321ae34f2f40aae80e18b6fa08b31c90095792ab64bb99d2e385143effaa", size = 31793, upload_time = "2023-11-06T01:38:56.163Z" }, - { url = "https://files.pythonhosted.org/packages/f9/d7/af9733f94df67a2e9e31ef47d4c41aff1836024f135cdbda4743eb628452/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eed24272b4121b7c22f234daed99899817d81d671b3ed030c876ac88bc9dc890", size = 33090, upload_time = "2023-11-06T01:38:57.091Z" }, - { url = "https://files.pythonhosted.org/packages/5b/6e/5b09b069a70028bcf05dbdc57a301fbe8b3bafecf916f2ed5a3065c79a71/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd13af06dab7c6ee92284fd02ed9a5613a07d5c1b41948dc8886e7207f86dfd", size = 29795, upload_time = "2023-11-06T01:38:58.278Z" }, - { url = "https://files.pythonhosted.org/packages/21/92/4690daefc2602f7c3429ecf54572d37a9e3c372d370344d2185daa4d5ecc/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1efc59bfba6aac33684d87b9e02813b0e2445b2f1c444dae2a0b396ad0ed60c", size = 31586, upload_time = "2023-11-06T01:38:59.363Z" }, - { url = "https://files.pythonhosted.org/packages/3c/67/0a29a91087196b02f278d8765120ee4e7486f1f72a4c505fd1cd3109e627/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfaf75ac574447afcf8ad998789071af11d2bcf6f947643231f692948839bd98", size = 36662, upload_time = "2023-11-06T01:39:00.795Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/8d56c514cd2333b652bd44c8f1962ab986cbe68e8ad7258c9e0f360cddb6/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c95f8751e2abd6f778da0399c8e0239321d560dbc58cb063827123137d213242", size = 35118, upload_time = "2023-11-06T01:39:01.883Z" }, - { url = "https://files.pythonhosted.org/packages/f5/9a/c7a175d10d503b86974cb07141ca175947145dd1c7370fcda86fbbcaf326/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:abd0c284b26b5c4ee806ca4f33ab5e16b4bf4d5ec9e093e75a6f6287acdde78e", size = 38198, upload_time = "2023-11-06T01:39:03.306Z" }, - { url = "https://files.pythonhosted.org/packages/fd/59/2e5086c8e8a05a7282a824a2a37e3c45cd5714e7b83d8bc0267cb3bb5b4f/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a47740652b25900ac5ce52667b2eade28d8b5fdca0ccd3323459df710e8210a", size = 36542, upload_time = "2023-11-06T01:39:04.751Z" }, - { url = "https://files.pythonhosted.org/packages/12/52/80d0a06e5f45fe7c278dd662da6ea5b39f2ff003248f448189932f6b71c2/lru_dict-1.3.0-cp311-cp311-win32.whl", hash = "sha256:a690c23fc353681ed8042d9fe8f48f0fb79a57b9a45daea2f0be1eef8a1a4aa4", size = 12533, upload_time = "2023-11-06T01:39:05.838Z" }, - { url = "https://files.pythonhosted.org/packages/ce/fe/1f12f33513310860ec6d722709ec4ad8256d9dcc3385f6ae2a244e6e66f5/lru_dict-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:efd3f4e0385d18f20f7ea6b08af2574c1bfaa5cb590102ef1bee781bdfba84bc", size = 13651, upload_time = "2023-11-06T01:39:06.871Z" }, - { url = "https://files.pythonhosted.org/packages/fc/5c/385f080747eb3083af87d8e4c9068f3c4cab89035f6982134889940dafd8/lru_dict-1.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c279068f68af3b46a5d649855e1fb87f5705fe1f744a529d82b2885c0e1fc69d", size = 17174, upload_time = "2023-11-06T01:39:07.923Z" }, - { url = "https://files.pythonhosted.org/packages/3c/de/5ef2ed75ce55d7059d1b96177ba04fa7ee1f35564f97bdfcd28fccfbe9d2/lru_dict-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:350e2233cfee9f326a0d7a08e309372d87186565e43a691b120006285a0ac549", size = 10742, upload_time = "2023-11-06T01:39:08.871Z" }, - { url = "https://files.pythonhosted.org/packages/ca/05/f69a6abb0062d2cf2ce0aaf0284b105b97d1da024ca6d3d0730e6151242e/lru_dict-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4eafb188a84483b3231259bf19030859f070321b00326dcb8e8c6cbf7db4b12f", size = 11079, upload_time = "2023-11-06T01:39:09.766Z" }, - { url = "https://files.pythonhosted.org/packages/ea/59/cf891143abe58a455b8eaa9175f0e80f624a146a2bf9a1ca842ee0ef930a/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73593791047e36b37fdc0b67b76aeed439fcea80959c7d46201240f9ec3b2563", size = 32469, upload_time = "2023-11-06T01:39:11.091Z" }, - { url = "https://files.pythonhosted.org/packages/59/88/d5976e9f70107ce11e45d93c6f0c2d5eaa1fc30bb3c8f57525eda4510dff/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1958cb70b9542773d6241974646e5410e41ef32e5c9e437d44040d59bd80daf2", size = 33496, upload_time = "2023-11-06T01:39:12.463Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f8/94d6e910d54fc1fa05c0ee1cd608c39401866a18cf5e5aff238449b33c11/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1cd3ed2cee78a47f11f3b70be053903bda197a873fd146e25c60c8e5a32cd6", size = 29914, upload_time = "2023-11-06T01:39:13.395Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b9/9db79780c8a3cfd66bba6847773061e5cf8a3746950273b9985d47bbfe53/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82eb230d48eaebd6977a92ddaa6d788f14cf4f4bcf5bbffa4ddfd60d051aa9d4", size = 32241, upload_time = "2023-11-06T01:39:14.612Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b6/08a623019daec22a40c4d6d2c40851dfa3d129a53b2f9469db8eb13666c1/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5ad659cbc349d0c9ba8e536b5f40f96a70c360f43323c29f4257f340d891531c", size = 37320, upload_time = "2023-11-06T01:39:15.875Z" }, - { url = "https://files.pythonhosted.org/packages/70/0b/d3717159c26155ff77679cee1b077d22e1008bf45f19921e193319cd8e46/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba490b8972531d153ac0d4e421f60d793d71a2f4adbe2f7740b3c55dce0a12f1", size = 35054, upload_time = "2023-11-06T01:39:17.063Z" }, - { url = "https://files.pythonhosted.org/packages/04/74/f2ae00de7c27984a19b88d2b09ac877031c525b01199d7841ec8fa657fd6/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:c0131351b8a7226c69f1eba5814cbc9d1d8daaf0fdec1ae3f30508e3de5262d4", size = 38613, upload_time = "2023-11-06T01:39:18.136Z" }, - { url = "https://files.pythonhosted.org/packages/5a/0b/e30236aafe31b4247aa9ae61ba8aac6dde75c3ea0e47a8fb7eef53f6d5ce/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e88dba16695f17f41701269fa046197a3fd7b34a8dba744c8749303ddaa18df", size = 37143, upload_time = "2023-11-06T01:39:19.571Z" }, - { url = "https://files.pythonhosted.org/packages/1c/28/b59bcebb8d76ba8147a784a8be7eab6a4ad3395b9236e73740ff675a5a52/lru_dict-1.3.0-cp312-cp312-win32.whl", hash = "sha256:6ffaf595e625b388babc8e7d79b40f26c7485f61f16efe76764e32dce9ea17fc", size = 12653, upload_time = "2023-11-06T01:39:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/bd/18/06d9710cb0a0d3634f8501e4bdcc07abe64a32e404d82895a6a36fab97f6/lru_dict-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf9da32ef2582434842ab6ba6e67290debfae72771255a8e8ab16f3e006de0aa", size = 13811, upload_time = "2023-11-06T01:39:21.599Z" }, + { url = "https://files.pythonhosted.org/packages/a8/c9/6fac0cb67160f0efa3cc76a6a7d04d5e21a516eeb991ebba08f4f8f01ec5/lru_dict-1.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:20c595764695d20bdc3ab9b582e0cc99814da183544afb83783a36d6741a0dac", size = 17750, upload-time = "2023-11-06T01:38:52.667Z" }, + { url = "https://files.pythonhosted.org/packages/61/14/f90dee4bc547ae266dbeffd4e11611234bb6af511dea48f3bc8dac1de478/lru_dict-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d9b30a8f50c3fa72a494eca6be5810a1b5c89e4f0fda89374f0d1c5ad8d37d51", size = 11055, upload-time = "2023-11-06T01:38:53.798Z" }, + { url = "https://files.pythonhosted.org/packages/4e/63/a0ae20525f9d52f62ac0def47935f8a2b3b6fcd2c145218b9a27fc1fb910/lru_dict-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9710737584650a4251b9a566cbb1a86f83437adb209c9ba43a4e756d12faf0d7", size = 11330, upload-time = "2023-11-06T01:38:54.847Z" }, + { url = "https://files.pythonhosted.org/packages/e9/c6/8c2b81b61e5206910c81b712500736227289aefe4ccfb36137aa21807003/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b84c321ae34f2f40aae80e18b6fa08b31c90095792ab64bb99d2e385143effaa", size = 31793, upload-time = "2023-11-06T01:38:56.163Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d7/af9733f94df67a2e9e31ef47d4c41aff1836024f135cdbda4743eb628452/lru_dict-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eed24272b4121b7c22f234daed99899817d81d671b3ed030c876ac88bc9dc890", size = 33090, upload-time = "2023-11-06T01:38:57.091Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6e/5b09b069a70028bcf05dbdc57a301fbe8b3bafecf916f2ed5a3065c79a71/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd13af06dab7c6ee92284fd02ed9a5613a07d5c1b41948dc8886e7207f86dfd", size = 29795, upload-time = "2023-11-06T01:38:58.278Z" }, + { url = "https://files.pythonhosted.org/packages/21/92/4690daefc2602f7c3429ecf54572d37a9e3c372d370344d2185daa4d5ecc/lru_dict-1.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1efc59bfba6aac33684d87b9e02813b0e2445b2f1c444dae2a0b396ad0ed60c", size = 31586, upload-time = "2023-11-06T01:38:59.363Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/0a29a91087196b02f278d8765120ee4e7486f1f72a4c505fd1cd3109e627/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfaf75ac574447afcf8ad998789071af11d2bcf6f947643231f692948839bd98", size = 36662, upload-time = "2023-11-06T01:39:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/8d56c514cd2333b652bd44c8f1962ab986cbe68e8ad7258c9e0f360cddb6/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c95f8751e2abd6f778da0399c8e0239321d560dbc58cb063827123137d213242", size = 35118, upload-time = "2023-11-06T01:39:01.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/9a/c7a175d10d503b86974cb07141ca175947145dd1c7370fcda86fbbcaf326/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:abd0c284b26b5c4ee806ca4f33ab5e16b4bf4d5ec9e093e75a6f6287acdde78e", size = 38198, upload-time = "2023-11-06T01:39:03.306Z" }, + { url = "https://files.pythonhosted.org/packages/fd/59/2e5086c8e8a05a7282a824a2a37e3c45cd5714e7b83d8bc0267cb3bb5b4f/lru_dict-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a47740652b25900ac5ce52667b2eade28d8b5fdca0ccd3323459df710e8210a", size = 36542, upload-time = "2023-11-06T01:39:04.751Z" }, + { url = "https://files.pythonhosted.org/packages/12/52/80d0a06e5f45fe7c278dd662da6ea5b39f2ff003248f448189932f6b71c2/lru_dict-1.3.0-cp311-cp311-win32.whl", hash = "sha256:a690c23fc353681ed8042d9fe8f48f0fb79a57b9a45daea2f0be1eef8a1a4aa4", size = 12533, upload-time = "2023-11-06T01:39:05.838Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fe/1f12f33513310860ec6d722709ec4ad8256d9dcc3385f6ae2a244e6e66f5/lru_dict-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:efd3f4e0385d18f20f7ea6b08af2574c1bfaa5cb590102ef1bee781bdfba84bc", size = 13651, upload-time = "2023-11-06T01:39:06.871Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5c/385f080747eb3083af87d8e4c9068f3c4cab89035f6982134889940dafd8/lru_dict-1.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c279068f68af3b46a5d649855e1fb87f5705fe1f744a529d82b2885c0e1fc69d", size = 17174, upload-time = "2023-11-06T01:39:07.923Z" }, + { url = "https://files.pythonhosted.org/packages/3c/de/5ef2ed75ce55d7059d1b96177ba04fa7ee1f35564f97bdfcd28fccfbe9d2/lru_dict-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:350e2233cfee9f326a0d7a08e309372d87186565e43a691b120006285a0ac549", size = 10742, upload-time = "2023-11-06T01:39:08.871Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/f69a6abb0062d2cf2ce0aaf0284b105b97d1da024ca6d3d0730e6151242e/lru_dict-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4eafb188a84483b3231259bf19030859f070321b00326dcb8e8c6cbf7db4b12f", size = 11079, upload-time = "2023-11-06T01:39:09.766Z" }, + { url = "https://files.pythonhosted.org/packages/ea/59/cf891143abe58a455b8eaa9175f0e80f624a146a2bf9a1ca842ee0ef930a/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73593791047e36b37fdc0b67b76aeed439fcea80959c7d46201240f9ec3b2563", size = 32469, upload-time = "2023-11-06T01:39:11.091Z" }, + { url = "https://files.pythonhosted.org/packages/59/88/d5976e9f70107ce11e45d93c6f0c2d5eaa1fc30bb3c8f57525eda4510dff/lru_dict-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1958cb70b9542773d6241974646e5410e41ef32e5c9e437d44040d59bd80daf2", size = 33496, upload-time = "2023-11-06T01:39:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/94d6e910d54fc1fa05c0ee1cd608c39401866a18cf5e5aff238449b33c11/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1cd3ed2cee78a47f11f3b70be053903bda197a873fd146e25c60c8e5a32cd6", size = 29914, upload-time = "2023-11-06T01:39:13.395Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b9/9db79780c8a3cfd66bba6847773061e5cf8a3746950273b9985d47bbfe53/lru_dict-1.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82eb230d48eaebd6977a92ddaa6d788f14cf4f4bcf5bbffa4ddfd60d051aa9d4", size = 32241, upload-time = "2023-11-06T01:39:14.612Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b6/08a623019daec22a40c4d6d2c40851dfa3d129a53b2f9469db8eb13666c1/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5ad659cbc349d0c9ba8e536b5f40f96a70c360f43323c29f4257f340d891531c", size = 37320, upload-time = "2023-11-06T01:39:15.875Z" }, + { url = "https://files.pythonhosted.org/packages/70/0b/d3717159c26155ff77679cee1b077d22e1008bf45f19921e193319cd8e46/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba490b8972531d153ac0d4e421f60d793d71a2f4adbe2f7740b3c55dce0a12f1", size = 35054, upload-time = "2023-11-06T01:39:17.063Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f2ae00de7c27984a19b88d2b09ac877031c525b01199d7841ec8fa657fd6/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:c0131351b8a7226c69f1eba5814cbc9d1d8daaf0fdec1ae3f30508e3de5262d4", size = 38613, upload-time = "2023-11-06T01:39:18.136Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0b/e30236aafe31b4247aa9ae61ba8aac6dde75c3ea0e47a8fb7eef53f6d5ce/lru_dict-1.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e88dba16695f17f41701269fa046197a3fd7b34a8dba744c8749303ddaa18df", size = 37143, upload-time = "2023-11-06T01:39:19.571Z" }, + { url = "https://files.pythonhosted.org/packages/1c/28/b59bcebb8d76ba8147a784a8be7eab6a4ad3395b9236e73740ff675a5a52/lru_dict-1.3.0-cp312-cp312-win32.whl", hash = "sha256:6ffaf595e625b388babc8e7d79b40f26c7485f61f16efe76764e32dce9ea17fc", size = 12653, upload-time = "2023-11-06T01:39:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/bd/18/06d9710cb0a0d3634f8501e4bdcc07abe64a32e404d82895a6a36fab97f6/lru_dict-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf9da32ef2582434842ab6ba6e67290debfae72771255a8e8ab16f3e006de0aa", size = 13811, upload-time = "2023-11-06T01:39:21.599Z" }, ] [[package]] name = "lxml" version = "5.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/3d/14e82fc7c8fb1b7761f7e748fd47e2ec8276d137b6acfe5a4bb73853e08f/lxml-5.4.0.tar.gz", hash = "sha256:d12832e1dbea4be280b22fd0ea7c9b87f0d8fc51ba06e92dc62d52f804f78ebd", size = 3679479, upload_time = "2025-04-23T01:50:29.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/3d/14e82fc7c8fb1b7761f7e748fd47e2ec8276d137b6acfe5a4bb73853e08f/lxml-5.4.0.tar.gz", hash = "sha256:d12832e1dbea4be280b22fd0ea7c9b87f0d8fc51ba06e92dc62d52f804f78ebd", size = 3679479, upload-time = "2025-04-23T01:50:29.322Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/2d/67693cc8a605a12e5975380d7ff83020dcc759351b5a066e1cced04f797b/lxml-5.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98a3912194c079ef37e716ed228ae0dcb960992100461b704aea4e93af6b0bb9", size = 8083240, upload_time = "2025-04-23T01:45:18.566Z" }, - { url = "https://files.pythonhosted.org/packages/73/53/b5a05ab300a808b72e848efd152fe9c022c0181b0a70b8bca1199f1bed26/lxml-5.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ea0252b51d296a75f6118ed0d8696888e7403408ad42345d7dfd0d1e93309a7", size = 4387685, upload_time = "2025-04-23T01:45:21.387Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/1a3879c5f512bdcd32995c301886fe082b2edd83c87d41b6d42d89b4ea4d/lxml-5.4.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92b69441d1bd39f4940f9eadfa417a25862242ca2c396b406f9272ef09cdcaa", size = 4991164, upload_time = "2025-04-23T01:45:23.849Z" }, - { url = "https://files.pythonhosted.org/packages/f9/94/bbc66e42559f9d04857071e3b3d0c9abd88579367fd2588a4042f641f57e/lxml-5.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20e16c08254b9b6466526bc1828d9370ee6c0d60a4b64836bc3ac2917d1e16df", size = 4746206, upload_time = "2025-04-23T01:45:26.361Z" }, - { url = "https://files.pythonhosted.org/packages/66/95/34b0679bee435da2d7cae895731700e519a8dfcab499c21662ebe671603e/lxml-5.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7605c1c32c3d6e8c990dd28a0970a3cbbf1429d5b92279e37fda05fb0c92190e", size = 5342144, upload_time = "2025-04-23T01:45:28.939Z" }, - { url = "https://files.pythonhosted.org/packages/e0/5d/abfcc6ab2fa0be72b2ba938abdae1f7cad4c632f8d552683ea295d55adfb/lxml-5.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecf4c4b83f1ab3d5a7ace10bafcb6f11df6156857a3c418244cef41ca9fa3e44", size = 4825124, upload_time = "2025-04-23T01:45:31.361Z" }, - { url = "https://files.pythonhosted.org/packages/5a/78/6bd33186c8863b36e084f294fc0a5e5eefe77af95f0663ef33809cc1c8aa/lxml-5.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cef4feae82709eed352cd7e97ae062ef6ae9c7b5dbe3663f104cd2c0e8d94ba", size = 4876520, upload_time = "2025-04-23T01:45:34.191Z" }, - { url = "https://files.pythonhosted.org/packages/3b/74/4d7ad4839bd0fc64e3d12da74fc9a193febb0fae0ba6ebd5149d4c23176a/lxml-5.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:df53330a3bff250f10472ce96a9af28628ff1f4efc51ccba351a8820bca2a8ba", size = 4765016, upload_time = "2025-04-23T01:45:36.7Z" }, - { url = "https://files.pythonhosted.org/packages/24/0d/0a98ed1f2471911dadfc541003ac6dd6879fc87b15e1143743ca20f3e973/lxml-5.4.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:aefe1a7cb852fa61150fcb21a8c8fcea7b58c4cb11fbe59c97a0a4b31cae3c8c", size = 5362884, upload_time = "2025-04-23T01:45:39.291Z" }, - { url = "https://files.pythonhosted.org/packages/48/de/d4f7e4c39740a6610f0f6959052b547478107967362e8424e1163ec37ae8/lxml-5.4.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ef5a7178fcc73b7d8c07229e89f8eb45b2908a9238eb90dcfc46571ccf0383b8", size = 4902690, upload_time = "2025-04-23T01:45:42.386Z" }, - { url = "https://files.pythonhosted.org/packages/07/8c/61763abd242af84f355ca4ef1ee096d3c1b7514819564cce70fd18c22e9a/lxml-5.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d2ed1b3cb9ff1c10e6e8b00941bb2e5bb568b307bfc6b17dffbbe8be5eecba86", size = 4944418, upload_time = "2025-04-23T01:45:46.051Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c5/6d7e3b63e7e282619193961a570c0a4c8a57fe820f07ca3fe2f6bd86608a/lxml-5.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:72ac9762a9f8ce74c9eed4a4e74306f2f18613a6b71fa065495a67ac227b3056", size = 4827092, upload_time = "2025-04-23T01:45:48.943Z" }, - { url = "https://files.pythonhosted.org/packages/71/4a/e60a306df54680b103348545706a98a7514a42c8b4fbfdcaa608567bb065/lxml-5.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f5cb182f6396706dc6cc1896dd02b1c889d644c081b0cdec38747573db88a7d7", size = 5418231, upload_time = "2025-04-23T01:45:51.481Z" }, - { url = "https://files.pythonhosted.org/packages/27/f2/9754aacd6016c930875854f08ac4b192a47fe19565f776a64004aa167521/lxml-5.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3a3178b4873df8ef9457a4875703488eb1622632a9cee6d76464b60e90adbfcd", size = 5261798, upload_time = "2025-04-23T01:45:54.146Z" }, - { url = "https://files.pythonhosted.org/packages/38/a2/0c49ec6941428b1bd4f280650d7b11a0f91ace9db7de32eb7aa23bcb39ff/lxml-5.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e094ec83694b59d263802ed03a8384594fcce477ce484b0cbcd0008a211ca751", size = 4988195, upload_time = "2025-04-23T01:45:56.685Z" }, - { url = "https://files.pythonhosted.org/packages/7a/75/87a3963a08eafc46a86c1131c6e28a4de103ba30b5ae903114177352a3d7/lxml-5.4.0-cp311-cp311-win32.whl", hash = "sha256:4329422de653cdb2b72afa39b0aa04252fca9071550044904b2e7036d9d97fe4", size = 3474243, upload_time = "2025-04-23T01:45:58.863Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f9/1f0964c4f6c2be861c50db380c554fb8befbea98c6404744ce243a3c87ef/lxml-5.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd3be6481ef54b8cfd0e1e953323b7aa9d9789b94842d0e5b142ef4bb7999539", size = 3815197, upload_time = "2025-04-23T01:46:01.096Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4c/d101ace719ca6a4ec043eb516fcfcb1b396a9fccc4fcd9ef593df34ba0d5/lxml-5.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5aff6f3e818e6bdbbb38e5967520f174b18f539c2b9de867b1e7fde6f8d95a4", size = 8127392, upload_time = "2025-04-23T01:46:04.09Z" }, - { url = "https://files.pythonhosted.org/packages/11/84/beddae0cec4dd9ddf46abf156f0af451c13019a0fa25d7445b655ba5ccb7/lxml-5.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942a5d73f739ad7c452bf739a62a0f83e2578afd6b8e5406308731f4ce78b16d", size = 4415103, upload_time = "2025-04-23T01:46:07.227Z" }, - { url = "https://files.pythonhosted.org/packages/d0/25/d0d93a4e763f0462cccd2b8a665bf1e4343dd788c76dcfefa289d46a38a9/lxml-5.4.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460508a4b07364d6abf53acaa0a90b6d370fafde5693ef37602566613a9b0779", size = 5024224, upload_time = "2025-04-23T01:46:10.237Z" }, - { url = "https://files.pythonhosted.org/packages/31/ce/1df18fb8f7946e7f3388af378b1f34fcf253b94b9feedb2cec5969da8012/lxml-5.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529024ab3a505fed78fe3cc5ddc079464e709f6c892733e3f5842007cec8ac6e", size = 4769913, upload_time = "2025-04-23T01:46:12.757Z" }, - { url = "https://files.pythonhosted.org/packages/4e/62/f4a6c60ae7c40d43657f552f3045df05118636be1165b906d3423790447f/lxml-5.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ca56ebc2c474e8f3d5761debfd9283b8b18c76c4fc0967b74aeafba1f5647f9", size = 5290441, upload_time = "2025-04-23T01:46:16.037Z" }, - { url = "https://files.pythonhosted.org/packages/9e/aa/04f00009e1e3a77838c7fc948f161b5d2d5de1136b2b81c712a263829ea4/lxml-5.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a81e1196f0a5b4167a8dafe3a66aa67c4addac1b22dc47947abd5d5c7a3f24b5", size = 4820165, upload_time = "2025-04-23T01:46:19.137Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/e0b2f61fa2404bf0f1fdf1898377e5bd1b74cc9b2cf2c6ba8509b8f27990/lxml-5.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b8686694423ddae324cf614e1b9659c2edb754de617703c3d29ff568448df5", size = 4932580, upload_time = "2025-04-23T01:46:21.963Z" }, - { url = "https://files.pythonhosted.org/packages/24/a2/8263f351b4ffe0ed3e32ea7b7830f845c795349034f912f490180d88a877/lxml-5.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c5681160758d3f6ac5b4fea370495c48aac0989d6a0f01bb9a72ad8ef5ab75c4", size = 4759493, upload_time = "2025-04-23T01:46:24.316Z" }, - { url = "https://files.pythonhosted.org/packages/05/00/41db052f279995c0e35c79d0f0fc9f8122d5b5e9630139c592a0b58c71b4/lxml-5.4.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:2dc191e60425ad70e75a68c9fd90ab284df64d9cd410ba8d2b641c0c45bc006e", size = 5324679, upload_time = "2025-04-23T01:46:27.097Z" }, - { url = "https://files.pythonhosted.org/packages/1d/be/ee99e6314cdef4587617d3b3b745f9356d9b7dd12a9663c5f3b5734b64ba/lxml-5.4.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:67f779374c6b9753ae0a0195a892a1c234ce8416e4448fe1e9f34746482070a7", size = 4890691, upload_time = "2025-04-23T01:46:30.009Z" }, - { url = "https://files.pythonhosted.org/packages/ad/36/239820114bf1d71f38f12208b9c58dec033cbcf80101cde006b9bde5cffd/lxml-5.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:79d5bfa9c1b455336f52343130b2067164040604e41f6dc4d8313867ed540079", size = 4955075, upload_time = "2025-04-23T01:46:32.33Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e1/1b795cc0b174efc9e13dbd078a9ff79a58728a033142bc6d70a1ee8fc34d/lxml-5.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d3c30ba1c9b48c68489dc1829a6eede9873f52edca1dda900066542528d6b20", size = 4838680, upload_time = "2025-04-23T01:46:34.852Z" }, - { url = "https://files.pythonhosted.org/packages/72/48/3c198455ca108cec5ae3662ae8acd7fd99476812fd712bb17f1b39a0b589/lxml-5.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1af80c6316ae68aded77e91cd9d80648f7dd40406cef73df841aa3c36f6907c8", size = 5391253, upload_time = "2025-04-23T01:46:37.608Z" }, - { url = "https://files.pythonhosted.org/packages/d6/10/5bf51858971c51ec96cfc13e800a9951f3fd501686f4c18d7d84fe2d6352/lxml-5.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4d885698f5019abe0de3d352caf9466d5de2baded00a06ef3f1216c1a58ae78f", size = 5261651, upload_time = "2025-04-23T01:46:40.183Z" }, - { url = "https://files.pythonhosted.org/packages/2b/11/06710dd809205377da380546f91d2ac94bad9ff735a72b64ec029f706c85/lxml-5.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea53d51859b6c64e7c51d522c03cc2c48b9b5d6172126854cc7f01aa11f52bc", size = 5024315, upload_time = "2025-04-23T01:46:43.333Z" }, - { url = "https://files.pythonhosted.org/packages/f5/b0/15b6217834b5e3a59ebf7f53125e08e318030e8cc0d7310355e6edac98ef/lxml-5.4.0-cp312-cp312-win32.whl", hash = "sha256:d90b729fd2732df28130c064aac9bb8aff14ba20baa4aee7bd0795ff1187545f", size = 3486149, upload_time = "2025-04-23T01:46:45.684Z" }, - { url = "https://files.pythonhosted.org/packages/91/1e/05ddcb57ad2f3069101611bd5f5084157d90861a2ef460bf42f45cced944/lxml-5.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1dc4ca99e89c335a7ed47d38964abcb36c5910790f9bd106f2a8fa2ee0b909d2", size = 3817095, upload_time = "2025-04-23T01:46:48.521Z" }, + { url = "https://files.pythonhosted.org/packages/81/2d/67693cc8a605a12e5975380d7ff83020dcc759351b5a066e1cced04f797b/lxml-5.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98a3912194c079ef37e716ed228ae0dcb960992100461b704aea4e93af6b0bb9", size = 8083240, upload-time = "2025-04-23T01:45:18.566Z" }, + { url = "https://files.pythonhosted.org/packages/73/53/b5a05ab300a808b72e848efd152fe9c022c0181b0a70b8bca1199f1bed26/lxml-5.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ea0252b51d296a75f6118ed0d8696888e7403408ad42345d7dfd0d1e93309a7", size = 4387685, upload-time = "2025-04-23T01:45:21.387Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/1a3879c5f512bdcd32995c301886fe082b2edd83c87d41b6d42d89b4ea4d/lxml-5.4.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92b69441d1bd39f4940f9eadfa417a25862242ca2c396b406f9272ef09cdcaa", size = 4991164, upload-time = "2025-04-23T01:45:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/f9/94/bbc66e42559f9d04857071e3b3d0c9abd88579367fd2588a4042f641f57e/lxml-5.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20e16c08254b9b6466526bc1828d9370ee6c0d60a4b64836bc3ac2917d1e16df", size = 4746206, upload-time = "2025-04-23T01:45:26.361Z" }, + { url = "https://files.pythonhosted.org/packages/66/95/34b0679bee435da2d7cae895731700e519a8dfcab499c21662ebe671603e/lxml-5.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7605c1c32c3d6e8c990dd28a0970a3cbbf1429d5b92279e37fda05fb0c92190e", size = 5342144, upload-time = "2025-04-23T01:45:28.939Z" }, + { url = "https://files.pythonhosted.org/packages/e0/5d/abfcc6ab2fa0be72b2ba938abdae1f7cad4c632f8d552683ea295d55adfb/lxml-5.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecf4c4b83f1ab3d5a7ace10bafcb6f11df6156857a3c418244cef41ca9fa3e44", size = 4825124, upload-time = "2025-04-23T01:45:31.361Z" }, + { url = "https://files.pythonhosted.org/packages/5a/78/6bd33186c8863b36e084f294fc0a5e5eefe77af95f0663ef33809cc1c8aa/lxml-5.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cef4feae82709eed352cd7e97ae062ef6ae9c7b5dbe3663f104cd2c0e8d94ba", size = 4876520, upload-time = "2025-04-23T01:45:34.191Z" }, + { url = "https://files.pythonhosted.org/packages/3b/74/4d7ad4839bd0fc64e3d12da74fc9a193febb0fae0ba6ebd5149d4c23176a/lxml-5.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:df53330a3bff250f10472ce96a9af28628ff1f4efc51ccba351a8820bca2a8ba", size = 4765016, upload-time = "2025-04-23T01:45:36.7Z" }, + { url = "https://files.pythonhosted.org/packages/24/0d/0a98ed1f2471911dadfc541003ac6dd6879fc87b15e1143743ca20f3e973/lxml-5.4.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:aefe1a7cb852fa61150fcb21a8c8fcea7b58c4cb11fbe59c97a0a4b31cae3c8c", size = 5362884, upload-time = "2025-04-23T01:45:39.291Z" }, + { url = "https://files.pythonhosted.org/packages/48/de/d4f7e4c39740a6610f0f6959052b547478107967362e8424e1163ec37ae8/lxml-5.4.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ef5a7178fcc73b7d8c07229e89f8eb45b2908a9238eb90dcfc46571ccf0383b8", size = 4902690, upload-time = "2025-04-23T01:45:42.386Z" }, + { url = "https://files.pythonhosted.org/packages/07/8c/61763abd242af84f355ca4ef1ee096d3c1b7514819564cce70fd18c22e9a/lxml-5.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d2ed1b3cb9ff1c10e6e8b00941bb2e5bb568b307bfc6b17dffbbe8be5eecba86", size = 4944418, upload-time = "2025-04-23T01:45:46.051Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/6d7e3b63e7e282619193961a570c0a4c8a57fe820f07ca3fe2f6bd86608a/lxml-5.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:72ac9762a9f8ce74c9eed4a4e74306f2f18613a6b71fa065495a67ac227b3056", size = 4827092, upload-time = "2025-04-23T01:45:48.943Z" }, + { url = "https://files.pythonhosted.org/packages/71/4a/e60a306df54680b103348545706a98a7514a42c8b4fbfdcaa608567bb065/lxml-5.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f5cb182f6396706dc6cc1896dd02b1c889d644c081b0cdec38747573db88a7d7", size = 5418231, upload-time = "2025-04-23T01:45:51.481Z" }, + { url = "https://files.pythonhosted.org/packages/27/f2/9754aacd6016c930875854f08ac4b192a47fe19565f776a64004aa167521/lxml-5.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3a3178b4873df8ef9457a4875703488eb1622632a9cee6d76464b60e90adbfcd", size = 5261798, upload-time = "2025-04-23T01:45:54.146Z" }, + { url = "https://files.pythonhosted.org/packages/38/a2/0c49ec6941428b1bd4f280650d7b11a0f91ace9db7de32eb7aa23bcb39ff/lxml-5.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e094ec83694b59d263802ed03a8384594fcce477ce484b0cbcd0008a211ca751", size = 4988195, upload-time = "2025-04-23T01:45:56.685Z" }, + { url = "https://files.pythonhosted.org/packages/7a/75/87a3963a08eafc46a86c1131c6e28a4de103ba30b5ae903114177352a3d7/lxml-5.4.0-cp311-cp311-win32.whl", hash = "sha256:4329422de653cdb2b72afa39b0aa04252fca9071550044904b2e7036d9d97fe4", size = 3474243, upload-time = "2025-04-23T01:45:58.863Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/1f0964c4f6c2be861c50db380c554fb8befbea98c6404744ce243a3c87ef/lxml-5.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd3be6481ef54b8cfd0e1e953323b7aa9d9789b94842d0e5b142ef4bb7999539", size = 3815197, upload-time = "2025-04-23T01:46:01.096Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4c/d101ace719ca6a4ec043eb516fcfcb1b396a9fccc4fcd9ef593df34ba0d5/lxml-5.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5aff6f3e818e6bdbbb38e5967520f174b18f539c2b9de867b1e7fde6f8d95a4", size = 8127392, upload-time = "2025-04-23T01:46:04.09Z" }, + { url = "https://files.pythonhosted.org/packages/11/84/beddae0cec4dd9ddf46abf156f0af451c13019a0fa25d7445b655ba5ccb7/lxml-5.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942a5d73f739ad7c452bf739a62a0f83e2578afd6b8e5406308731f4ce78b16d", size = 4415103, upload-time = "2025-04-23T01:46:07.227Z" }, + { url = "https://files.pythonhosted.org/packages/d0/25/d0d93a4e763f0462cccd2b8a665bf1e4343dd788c76dcfefa289d46a38a9/lxml-5.4.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460508a4b07364d6abf53acaa0a90b6d370fafde5693ef37602566613a9b0779", size = 5024224, upload-time = "2025-04-23T01:46:10.237Z" }, + { url = "https://files.pythonhosted.org/packages/31/ce/1df18fb8f7946e7f3388af378b1f34fcf253b94b9feedb2cec5969da8012/lxml-5.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529024ab3a505fed78fe3cc5ddc079464e709f6c892733e3f5842007cec8ac6e", size = 4769913, upload-time = "2025-04-23T01:46:12.757Z" }, + { url = "https://files.pythonhosted.org/packages/4e/62/f4a6c60ae7c40d43657f552f3045df05118636be1165b906d3423790447f/lxml-5.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ca56ebc2c474e8f3d5761debfd9283b8b18c76c4fc0967b74aeafba1f5647f9", size = 5290441, upload-time = "2025-04-23T01:46:16.037Z" }, + { url = "https://files.pythonhosted.org/packages/9e/aa/04f00009e1e3a77838c7fc948f161b5d2d5de1136b2b81c712a263829ea4/lxml-5.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a81e1196f0a5b4167a8dafe3a66aa67c4addac1b22dc47947abd5d5c7a3f24b5", size = 4820165, upload-time = "2025-04-23T01:46:19.137Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/e0b2f61fa2404bf0f1fdf1898377e5bd1b74cc9b2cf2c6ba8509b8f27990/lxml-5.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b8686694423ddae324cf614e1b9659c2edb754de617703c3d29ff568448df5", size = 4932580, upload-time = "2025-04-23T01:46:21.963Z" }, + { url = "https://files.pythonhosted.org/packages/24/a2/8263f351b4ffe0ed3e32ea7b7830f845c795349034f912f490180d88a877/lxml-5.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c5681160758d3f6ac5b4fea370495c48aac0989d6a0f01bb9a72ad8ef5ab75c4", size = 4759493, upload-time = "2025-04-23T01:46:24.316Z" }, + { url = "https://files.pythonhosted.org/packages/05/00/41db052f279995c0e35c79d0f0fc9f8122d5b5e9630139c592a0b58c71b4/lxml-5.4.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:2dc191e60425ad70e75a68c9fd90ab284df64d9cd410ba8d2b641c0c45bc006e", size = 5324679, upload-time = "2025-04-23T01:46:27.097Z" }, + { url = "https://files.pythonhosted.org/packages/1d/be/ee99e6314cdef4587617d3b3b745f9356d9b7dd12a9663c5f3b5734b64ba/lxml-5.4.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:67f779374c6b9753ae0a0195a892a1c234ce8416e4448fe1e9f34746482070a7", size = 4890691, upload-time = "2025-04-23T01:46:30.009Z" }, + { url = "https://files.pythonhosted.org/packages/ad/36/239820114bf1d71f38f12208b9c58dec033cbcf80101cde006b9bde5cffd/lxml-5.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:79d5bfa9c1b455336f52343130b2067164040604e41f6dc4d8313867ed540079", size = 4955075, upload-time = "2025-04-23T01:46:32.33Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e1/1b795cc0b174efc9e13dbd078a9ff79a58728a033142bc6d70a1ee8fc34d/lxml-5.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d3c30ba1c9b48c68489dc1829a6eede9873f52edca1dda900066542528d6b20", size = 4838680, upload-time = "2025-04-23T01:46:34.852Z" }, + { url = "https://files.pythonhosted.org/packages/72/48/3c198455ca108cec5ae3662ae8acd7fd99476812fd712bb17f1b39a0b589/lxml-5.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1af80c6316ae68aded77e91cd9d80648f7dd40406cef73df841aa3c36f6907c8", size = 5391253, upload-time = "2025-04-23T01:46:37.608Z" }, + { url = "https://files.pythonhosted.org/packages/d6/10/5bf51858971c51ec96cfc13e800a9951f3fd501686f4c18d7d84fe2d6352/lxml-5.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4d885698f5019abe0de3d352caf9466d5de2baded00a06ef3f1216c1a58ae78f", size = 5261651, upload-time = "2025-04-23T01:46:40.183Z" }, + { url = "https://files.pythonhosted.org/packages/2b/11/06710dd809205377da380546f91d2ac94bad9ff735a72b64ec029f706c85/lxml-5.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea53d51859b6c64e7c51d522c03cc2c48b9b5d6172126854cc7f01aa11f52bc", size = 5024315, upload-time = "2025-04-23T01:46:43.333Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b0/15b6217834b5e3a59ebf7f53125e08e318030e8cc0d7310355e6edac98ef/lxml-5.4.0-cp312-cp312-win32.whl", hash = "sha256:d90b729fd2732df28130c064aac9bb8aff14ba20baa4aee7bd0795ff1187545f", size = 3486149, upload-time = "2025-04-23T01:46:45.684Z" }, + { url = "https://files.pythonhosted.org/packages/91/1e/05ddcb57ad2f3069101611bd5f5084157d90861a2ef460bf42f45cced944/lxml-5.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1dc4ca99e89c335a7ed47d38964abcb36c5910790f9bd106f2a8fa2ee0b909d2", size = 3817095, upload-time = "2025-04-23T01:46:48.521Z" }, ] [[package]] name = "markdown" version = "3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906, upload_time = "2025-04-11T14:42:50.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906, upload-time = "2025-04-11T14:42:50.928Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210, upload_time = "2025-04-11T14:42:49.178Z" }, + { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210, upload-time = "2025-04-11T14:42:49.178Z" }, ] [[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, upload_time = "2024-10-18T15:21:54.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } wheels = [ - { 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, upload_time = "2024-10-18T15:21:02.187Z" }, - { 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, upload_time = "2024-10-18T15:21:02.941Z" }, - { 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, upload_time = "2024-10-18T15:21:03.953Z" }, - { 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, upload_time = "2024-10-18T15:21:06.495Z" }, - { 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, upload_time = "2024-10-18T15:21:07.295Z" }, - { 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, upload_time = "2024-10-18T15:21:08.073Z" }, - { 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, upload_time = "2024-10-18T15:21:09.318Z" }, - { 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, upload_time = "2024-10-18T15:21:10.185Z" }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload_time = "2024-10-18T15:21:11.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload_time = "2024-10-18T15:21:12.911Z" }, - { 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, upload_time = "2024-10-18T15:21:13.777Z" }, - { 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, upload_time = "2024-10-18T15:21:14.822Z" }, - { 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, upload_time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload_time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload_time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload_time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload_time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload_time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload_time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload_time = "2024-10-18T15:21:23.499Z" }, + { 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, upload-time = "2024-10-18T15:21:02.187Z" }, + { 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, upload-time = "2024-10-18T15:21:02.941Z" }, + { 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, upload-time = "2024-10-18T15:21:03.953Z" }, + { 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, upload-time = "2024-10-18T15:21:06.495Z" }, + { 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, upload-time = "2024-10-18T15:21:07.295Z" }, + { 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, upload-time = "2024-10-18T15:21:08.073Z" }, + { 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, upload-time = "2024-10-18T15:21:09.318Z" }, + { 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, upload-time = "2024-10-18T15:21:10.185Z" }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, + { 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, upload-time = "2024-10-18T15:21:13.777Z" }, + { 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, upload-time = "2024-10-18T15:21:14.822Z" }, + { 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, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, ] [[package]] @@ -934,29 +934,29 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335, upload_time = "2025-02-27T19:19:51.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335, upload-time = "2025-02-27T19:19:51.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669, upload_time = "2025-02-27T19:18:34.346Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996, upload_time = "2025-02-27T19:18:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612, upload_time = "2025-02-27T19:18:39.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/b8/53fa08a5eaf78d3a7213fd6da1feec4bae14a81d9805e567013811ff0e85/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c", size = 8602258, upload_time = "2025-02-27T19:18:43.217Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/4397d2ce808467af86684a622dd112664553e81752ea8bf61bdd89d24a41/matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7", size = 9408896, upload_time = "2025-02-27T19:18:45.852Z" }, - { url = "https://files.pythonhosted.org/packages/d7/68/0d03098b3feb786cbd494df0aac15b571effda7f7cbdec267e8a8d398c16/matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a", size = 8061281, upload_time = "2025-02-27T19:18:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/7c/1d/5e0dc3b59c034e43de16f94deb68f4ad8a96b3ea00f4b37c160b7474928e/matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107", size = 8175488, upload_time = "2025-02-27T19:18:51.436Z" }, - { url = "https://files.pythonhosted.org/packages/7a/81/dae7e14042e74da658c3336ab9799128e09a1ee03964f2d89630b5d12106/matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be", size = 8046264, upload_time = "2025-02-27T19:18:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/21/c4/22516775dcde10fc9c9571d155f90710761b028fc44f660508106c363c97/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6", size = 8452048, upload_time = "2025-02-27T19:18:56.536Z" }, - { url = "https://files.pythonhosted.org/packages/63/23/c0615001f67ce7c96b3051d856baedc0c818a2ed84570b9bf9bde200f85d/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d", size = 8597111, upload_time = "2025-02-27T19:18:59.439Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c0/a07939a82aed77770514348f4568177d7dadab9787ebc618a616fe3d665e/matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea", size = 9402771, upload_time = "2025-02-27T19:19:01.944Z" }, - { url = "https://files.pythonhosted.org/packages/a6/b6/a9405484fb40746fdc6ae4502b16a9d6e53282ba5baaf9ebe2da579f68c4/matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c", size = 8063742, upload_time = "2025-02-27T19:19:04.632Z" }, + { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669, upload-time = "2025-02-27T19:18:34.346Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996, upload-time = "2025-02-27T19:18:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612, upload-time = "2025-02-27T19:18:39.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/b8/53fa08a5eaf78d3a7213fd6da1feec4bae14a81d9805e567013811ff0e85/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c", size = 8602258, upload-time = "2025-02-27T19:18:43.217Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/4397d2ce808467af86684a622dd112664553e81752ea8bf61bdd89d24a41/matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7", size = 9408896, upload-time = "2025-02-27T19:18:45.852Z" }, + { url = "https://files.pythonhosted.org/packages/d7/68/0d03098b3feb786cbd494df0aac15b571effda7f7cbdec267e8a8d398c16/matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a", size = 8061281, upload-time = "2025-02-27T19:18:48.919Z" }, + { url = "https://files.pythonhosted.org/packages/7c/1d/5e0dc3b59c034e43de16f94deb68f4ad8a96b3ea00f4b37c160b7474928e/matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107", size = 8175488, upload-time = "2025-02-27T19:18:51.436Z" }, + { url = "https://files.pythonhosted.org/packages/7a/81/dae7e14042e74da658c3336ab9799128e09a1ee03964f2d89630b5d12106/matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be", size = 8046264, upload-time = "2025-02-27T19:18:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/21/c4/22516775dcde10fc9c9571d155f90710761b028fc44f660508106c363c97/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6", size = 8452048, upload-time = "2025-02-27T19:18:56.536Z" }, + { url = "https://files.pythonhosted.org/packages/63/23/c0615001f67ce7c96b3051d856baedc0c818a2ed84570b9bf9bde200f85d/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d", size = 8597111, upload-time = "2025-02-27T19:18:59.439Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c0/a07939a82aed77770514348f4568177d7dadab9787ebc618a616fe3d665e/matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea", size = 9402771, upload-time = "2025-02-27T19:19:01.944Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b6/a9405484fb40746fdc6ae4502b16a9d6e53282ba5baaf9ebe2da579f68c4/matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c", size = 8063742, upload-time = "2025-02-27T19:19:04.632Z" }, ] [[package]] name = "mergedeep" version = "1.3.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload_time = "2021-02-05T18:55:30.623Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload_time = "2021-02-05T18:55:29.583Z" }, + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, ] [[package]] @@ -1032,9 +1032,9 @@ dependencies = [ { name = "pyyaml-env-tag" }, { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload_time = "2024-08-30T12:24:06.899Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload_time = "2024-08-30T12:24:05.054Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, ] [[package]] @@ -1046,9 +1046,9 @@ dependencies = [ { name = "platformdirs" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload_time = "2023-11-20T17:51:09.981Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload_time = "2023-11-20T17:51:08.587Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, ] [[package]] @@ -1060,15 +1060,15 @@ dependencies = [ { name = "python3-xlib", marker = "sys_platform == 'linux'" }, { name = "rubicon-objc", marker = "sys_platform == 'darwin'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850, upload_time = "2020-03-27T21:20:10.136Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz", hash = "sha256:2c62fb8885062b8e520a3cce0a297c657adcc08c60952eb05bc8256ef6f7f6e7", size = 10850, upload-time = "2020-03-27T21:20:10.136Z" } [[package]] name = "mpmath" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload_time = "2023-03-07T16:47:11.061Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload_time = "2023-03-07T16:47:09.197Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] [[package]] @@ -1080,9 +1080,9 @@ dependencies = [ { name = "pyjwt", extra = ["crypto"] }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/90/81dcc50f0be11a8c4dcbae1a9f761a26e5f905231330a7cacc9f04ec4c61/msal-1.32.3.tar.gz", hash = "sha256:5eea038689c78a5a70ca8ecbe1245458b55a857bd096efb6989c69ba15985d35", size = 151449, upload_time = "2025-04-25T13:12:34.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/90/81dcc50f0be11a8c4dcbae1a9f761a26e5f905231330a7cacc9f04ec4c61/msal-1.32.3.tar.gz", hash = "sha256:5eea038689c78a5a70ca8ecbe1245458b55a857bd096efb6989c69ba15985d35", size = 151449, upload-time = "2025-04-25T13:12:34.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/bf/81516b9aac7fd867709984d08eb4db1d2e3fe1df795c8e442cde9b568962/msal-1.32.3-py3-none-any.whl", hash = "sha256:b2798db57760b1961b142f027ffb7c8169536bf77316e99a0df5c4aaebb11569", size = 115358, upload_time = "2025-04-25T13:12:33.034Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/81516b9aac7fd867709984d08eb4db1d2e3fe1df795c8e442cde9b568962/msal-1.32.3-py3-none-any.whl", hash = "sha256:b2798db57760b1961b142f027ffb7c8169536bf77316e99a0df5c4aaebb11569", size = 115358, upload-time = "2025-04-25T13:12:33.034Z" }, ] [[package]] @@ -1092,52 +1092,52 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "msal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload_time = "2025-03-14T23:51:03.902Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload_time = "2025-03-14T23:51:03.016Z" }, + { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, ] [[package]] name = "multidict" version = "6.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372, upload_time = "2025-04-10T22:20:17.956Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372, upload-time = "2025-04-10T22:20:17.956Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259, upload_time = "2025-04-10T22:17:59.632Z" }, - { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451, upload_time = "2025-04-10T22:18:01.202Z" }, - { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706, upload_time = "2025-04-10T22:18:02.276Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669, upload_time = "2025-04-10T22:18:03.436Z" }, - { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182, upload_time = "2025-04-10T22:18:04.922Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025, upload_time = "2025-04-10T22:18:06.274Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481, upload_time = "2025-04-10T22:18:07.742Z" }, - { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492, upload_time = "2025-04-10T22:18:09.095Z" }, - { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279, upload_time = "2025-04-10T22:18:10.474Z" }, - { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733, upload_time = "2025-04-10T22:18:11.793Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089, upload_time = "2025-04-10T22:18:13.153Z" }, - { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257, upload_time = "2025-04-10T22:18:14.654Z" }, - { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728, upload_time = "2025-04-10T22:18:16.236Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087, upload_time = "2025-04-10T22:18:17.979Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137, upload_time = "2025-04-10T22:18:19.362Z" }, - { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959, upload_time = "2025-04-10T22:18:20.728Z" }, - { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541, upload_time = "2025-04-10T22:18:22.001Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019, upload_time = "2025-04-10T22:18:23.174Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925, upload_time = "2025-04-10T22:18:24.834Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008, upload_time = "2025-04-10T22:18:26.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374, upload_time = "2025-04-10T22:18:27.714Z" }, - { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869, upload_time = "2025-04-10T22:18:29.162Z" }, - { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949, upload_time = "2025-04-10T22:18:30.679Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032, upload_time = "2025-04-10T22:18:32.146Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517, upload_time = "2025-04-10T22:18:33.538Z" }, - { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291, upload_time = "2025-04-10T22:18:34.962Z" }, - { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982, upload_time = "2025-04-10T22:18:36.443Z" }, - { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823, upload_time = "2025-04-10T22:18:37.924Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714, upload_time = "2025-04-10T22:18:39.807Z" }, - { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739, upload_time = "2025-04-10T22:18:41.341Z" }, - { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809, upload_time = "2025-04-10T22:18:42.817Z" }, - { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934, upload_time = "2025-04-10T22:18:44.311Z" }, - { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242, upload_time = "2025-04-10T22:18:46.193Z" }, - { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635, upload_time = "2025-04-10T22:18:47.498Z" }, - { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400, upload_time = "2025-04-10T22:20:16.445Z" }, + { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259, upload-time = "2025-04-10T22:17:59.632Z" }, + { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451, upload-time = "2025-04-10T22:18:01.202Z" }, + { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706, upload-time = "2025-04-10T22:18:02.276Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669, upload-time = "2025-04-10T22:18:03.436Z" }, + { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182, upload-time = "2025-04-10T22:18:04.922Z" }, + { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025, upload-time = "2025-04-10T22:18:06.274Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481, upload-time = "2025-04-10T22:18:07.742Z" }, + { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492, upload-time = "2025-04-10T22:18:09.095Z" }, + { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279, upload-time = "2025-04-10T22:18:10.474Z" }, + { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733, upload-time = "2025-04-10T22:18:11.793Z" }, + { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089, upload-time = "2025-04-10T22:18:13.153Z" }, + { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257, upload-time = "2025-04-10T22:18:14.654Z" }, + { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728, upload-time = "2025-04-10T22:18:16.236Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087, upload-time = "2025-04-10T22:18:17.979Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137, upload-time = "2025-04-10T22:18:19.362Z" }, + { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959, upload-time = "2025-04-10T22:18:20.728Z" }, + { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541, upload-time = "2025-04-10T22:18:22.001Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019, upload-time = "2025-04-10T22:18:23.174Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925, upload-time = "2025-04-10T22:18:24.834Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008, upload-time = "2025-04-10T22:18:26.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374, upload-time = "2025-04-10T22:18:27.714Z" }, + { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869, upload-time = "2025-04-10T22:18:29.162Z" }, + { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949, upload-time = "2025-04-10T22:18:30.679Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032, upload-time = "2025-04-10T22:18:32.146Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517, upload-time = "2025-04-10T22:18:33.538Z" }, + { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291, upload-time = "2025-04-10T22:18:34.962Z" }, + { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982, upload-time = "2025-04-10T22:18:36.443Z" }, + { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823, upload-time = "2025-04-10T22:18:37.924Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714, upload-time = "2025-04-10T22:18:39.807Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739, upload-time = "2025-04-10T22:18:41.341Z" }, + { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809, upload-time = "2025-04-10T22:18:42.817Z" }, + { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934, upload-time = "2025-04-10T22:18:44.311Z" }, + { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242, upload-time = "2025-04-10T22:18:46.193Z" }, + { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635, upload-time = "2025-04-10T22:18:47.498Z" }, + { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400, upload-time = "2025-04-10T22:20:16.445Z" }, ] [[package]] @@ -1148,67 +1148,67 @@ dependencies = [ { name = "mypy-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload_time = "2025-02-05T03:50:34.655Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717, upload-time = "2025-02-05T03:50:34.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload_time = "2025-02-05T03:50:17.287Z" }, - { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload_time = "2025-02-05T03:49:51.21Z" }, - { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload_time = "2025-02-05T03:50:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload_time = "2025-02-05T03:49:42.408Z" }, - { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload_time = "2025-02-05T03:49:07.707Z" }, - { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload_time = "2025-02-05T03:49:54.581Z" }, - { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload_time = "2025-02-05T03:50:28.25Z" }, - { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload_time = "2025-02-05T03:50:13.411Z" }, - { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload_time = "2025-02-05T03:50:31.421Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload_time = "2025-02-05T03:48:48.705Z" }, - { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload_time = "2025-02-05T03:49:03.628Z" }, - { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload_time = "2025-02-05T03:50:00.313Z" }, - { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload_time = "2025-02-05T03:50:08.348Z" }, + { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338, upload-time = "2025-02-05T03:50:17.287Z" }, + { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540, upload-time = "2025-02-05T03:49:51.21Z" }, + { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051, upload-time = "2025-02-05T03:50:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751, upload-time = "2025-02-05T03:49:42.408Z" }, + { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783, upload-time = "2025-02-05T03:49:07.707Z" }, + { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618, upload-time = "2025-02-05T03:49:54.581Z" }, + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981, upload-time = "2025-02-05T03:50:28.25Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175, upload-time = "2025-02-05T03:50:13.411Z" }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675, upload-time = "2025-02-05T03:50:31.421Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020, upload-time = "2025-02-05T03:48:48.705Z" }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582, upload-time = "2025-02-05T03:49:03.628Z" }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614, upload-time = "2025-02-05T03:50:00.313Z" }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777, upload-time = "2025-02-05T03:50:08.348Z" }, ] [[package]] name = "mypy-extensions" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload_time = "2025-04-22T14:54:24.164Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload_time = "2025-04-22T14:54:22.983Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "natsort" version = "8.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload_time = "2023-06-20T04:17:19.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload_time = "2023-06-20T04:17:17.522Z" }, + { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" }, ] [[package]] name = "numpy" version = "2.1.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090, upload_time = "2024-11-02T17:48:55.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090, upload-time = "2024-11-02T17:48:55.832Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252, upload_time = "2024-11-02T17:34:01.372Z" }, - { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119, upload_time = "2024-11-02T17:34:23.809Z" }, - { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978, upload_time = "2024-11-02T17:34:34.001Z" }, - { url = "https://files.pythonhosted.org/packages/09/ac/61d07930a4993dd9691a6432de16d93bbe6aa4b1c12a5e573d468eefc1ca/numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09", size = 6892570, upload_time = "2024-11-02T17:34:45.401Z" }, - { url = "https://files.pythonhosted.org/packages/27/2f/21b94664f23af2bb52030653697c685022119e0dc93d6097c3cb45bce5f9/numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a", size = 13896715, upload_time = "2024-11-02T17:35:06.564Z" }, - { url = "https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b", size = 16339644, upload_time = "2024-11-02T17:35:30.888Z" }, - { url = "https://files.pythonhosted.org/packages/fa/81/ce213159a1ed8eb7d88a2a6ef4fbdb9e4ffd0c76b866c350eb4e3c37e640/numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee", size = 16712217, upload_time = "2024-11-02T17:35:56.703Z" }, - { url = "https://files.pythonhosted.org/packages/7d/84/4de0b87d5a72f45556b2a8ee9fc8801e8518ec867fc68260c1f5dcb3903f/numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0", size = 14399053, upload_time = "2024-11-02T17:36:22.3Z" }, - { url = "https://files.pythonhosted.org/packages/7e/1c/e5fabb9ad849f9d798b44458fd12a318d27592d4bc1448e269dec070ff04/numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9", size = 6534741, upload_time = "2024-11-02T17:36:33.552Z" }, - { url = "https://files.pythonhosted.org/packages/1e/48/a9a4b538e28f854bfb62e1dea3c8fea12e90216a276c7777ae5345ff29a7/numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2", size = 12869487, upload_time = "2024-11-02T17:36:52.909Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658, upload_time = "2024-11-02T17:37:23.919Z" }, - { url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258, upload_time = "2024-11-02T17:37:45.252Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249, upload_time = "2024-11-02T17:37:54.252Z" }, - { url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704, upload_time = "2024-11-02T17:38:05.127Z" }, - { url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089, upload_time = "2024-11-02T17:38:25.997Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185, upload_time = "2024-11-02T17:38:51.07Z" }, - { url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751, upload_time = "2024-11-02T17:39:15.801Z" }, - { url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705, upload_time = "2024-11-02T17:39:38.274Z" }, - { url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077, upload_time = "2024-11-02T17:39:49.299Z" }, - { url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858, upload_time = "2024-11-02T17:40:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/ad/81/c8167192eba5247593cd9d305ac236847c2912ff39e11402e72ae28a4985/numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d", size = 21156252, upload-time = "2024-11-02T17:34:01.372Z" }, + { url = "https://files.pythonhosted.org/packages/da/74/5a60003fc3d8a718d830b08b654d0eea2d2db0806bab8f3c2aca7e18e010/numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41", size = 13784119, upload-time = "2024-11-02T17:34:23.809Z" }, + { url = "https://files.pythonhosted.org/packages/47/7c/864cb966b96fce5e63fcf25e1e4d957fe5725a635e5f11fe03f39dd9d6b5/numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9", size = 5352978, upload-time = "2024-11-02T17:34:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/09/ac/61d07930a4993dd9691a6432de16d93bbe6aa4b1c12a5e573d468eefc1ca/numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09", size = 6892570, upload-time = "2024-11-02T17:34:45.401Z" }, + { url = "https://files.pythonhosted.org/packages/27/2f/21b94664f23af2bb52030653697c685022119e0dc93d6097c3cb45bce5f9/numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a", size = 13896715, upload-time = "2024-11-02T17:35:06.564Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b", size = 16339644, upload-time = "2024-11-02T17:35:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/fa/81/ce213159a1ed8eb7d88a2a6ef4fbdb9e4ffd0c76b866c350eb4e3c37e640/numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee", size = 16712217, upload-time = "2024-11-02T17:35:56.703Z" }, + { url = "https://files.pythonhosted.org/packages/7d/84/4de0b87d5a72f45556b2a8ee9fc8801e8518ec867fc68260c1f5dcb3903f/numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0", size = 14399053, upload-time = "2024-11-02T17:36:22.3Z" }, + { url = "https://files.pythonhosted.org/packages/7e/1c/e5fabb9ad849f9d798b44458fd12a318d27592d4bc1448e269dec070ff04/numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9", size = 6534741, upload-time = "2024-11-02T17:36:33.552Z" }, + { url = "https://files.pythonhosted.org/packages/1e/48/a9a4b538e28f854bfb62e1dea3c8fea12e90216a276c7777ae5345ff29a7/numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2", size = 12869487, upload-time = "2024-11-02T17:36:52.909Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658, upload-time = "2024-11-02T17:37:23.919Z" }, + { url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258, upload-time = "2024-11-02T17:37:45.252Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249, upload-time = "2024-11-02T17:37:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704, upload-time = "2024-11-02T17:38:05.127Z" }, + { url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089, upload-time = "2024-11-02T17:38:25.997Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185, upload-time = "2024-11-02T17:38:51.07Z" }, + { url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751, upload-time = "2024-11-02T17:39:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705, upload-time = "2024-11-02T17:39:38.274Z" }, + { url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077, upload-time = "2024-11-02T17:39:49.299Z" }, + { url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858, upload-time = "2024-11-02T17:40:08.851Z" }, ] [[package]] @@ -1219,18 +1219,18 @@ dependencies = [ { name = "numpy" }, { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/54/0e385c26bf230d223810a9c7d06628d954008a5e5e4b73ee26ef02327282/onnx-1.17.0.tar.gz", hash = "sha256:48ca1a91ff73c1d5e3ea2eef20ae5d0e709bb8a2355ed798ffc2169753013fd3", size = 12165120, upload_time = "2024-10-01T21:48:40.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/54/0e385c26bf230d223810a9c7d06628d954008a5e5e4b73ee26ef02327282/onnx-1.17.0.tar.gz", hash = "sha256:48ca1a91ff73c1d5e3ea2eef20ae5d0e709bb8a2355ed798ffc2169753013fd3", size = 12165120, upload-time = "2024-10-01T21:48:40.63Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a9/8d1b1d53aec70df53e0f57e9f9fcf47004276539e29230c3d5f1f50719ba/onnx-1.17.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:d6fc3a03fc0129b8b6ac03f03bc894431ffd77c7d79ec023d0afd667b4d35869", size = 16647991, upload_time = "2024-10-01T21:46:02.491Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e3/cc80110e5996ca61878f7b4c73c7a286cd88918ff35eacb60dc75ab11ef5/onnx-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a4b63d4e1d8ec3e2f069e7b798b2955810aa434f7361f01bc8ca08d69cce4", size = 15908949, upload_time = "2024-10-01T21:46:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/b1/2f/91092557ed478e323a2b4471e2081fdf88d1dd52ae988ceaf7db4e4506ff/onnx-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a183c6178be001bf398260e5ac2c927dc43e7746e8638d6c05c20e321f8c949", size = 16048190, upload_time = "2024-10-01T21:46:08.041Z" }, - { url = "https://files.pythonhosted.org/packages/ac/59/9ea23fc22d0bb853133f363e6248e31bcbc6c1c90543a3938c00412ac02a/onnx-1.17.0-cp311-cp311-win32.whl", hash = "sha256:081ec43a8b950171767d99075b6b92553901fa429d4bc5eb3ad66b36ef5dbe3a", size = 14424299, upload_time = "2024-10-01T21:46:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/51/a5/19b0dfcb567b62e7adf1a21b08b23224f0c2d13842aee4d0abc6f07f9cf5/onnx-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:95c03e38671785036bb704c30cd2e150825f6ab4763df3a4f1d249da48525957", size = 14529142, upload_time = "2024-10-01T21:46:12.574Z" }, - { url = "https://files.pythonhosted.org/packages/b4/dd/c416a11a28847fafb0db1bf43381979a0f522eb9107b831058fde012dd56/onnx-1.17.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:0e906e6a83437de05f8139ea7eaf366bf287f44ae5cc44b2850a30e296421f2f", size = 16651271, upload_time = "2024-10-01T21:46:16.084Z" }, - { url = "https://files.pythonhosted.org/packages/f0/6c/f040652277f514ecd81b7251841f96caa5538365af7df07f86c6018cda2b/onnx-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d955ba2939878a520a97614bcf2e79c1df71b29203e8ced478fa78c9a9c63c2", size = 15907522, upload_time = "2024-10-01T21:46:18.574Z" }, - { url = "https://files.pythonhosted.org/packages/3d/7c/67f4952d1b56b3f74a154b97d0dd0630d525923b354db117d04823b8b49b/onnx-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3fb5cc4e2898ac5312a7dc03a65133dd2abf9a5e520e69afb880a7251ec97a", size = 16046307, upload_time = "2024-10-01T21:46:21.186Z" }, - { url = "https://files.pythonhosted.org/packages/ae/20/6da11042d2ab870dfb4ce4a6b52354d7651b6b4112038b6d2229ab9904c4/onnx-1.17.0-cp312-cp312-win32.whl", hash = "sha256:317870fca3349d19325a4b7d1b5628f6de3811e9710b1e3665c68b073d0e68d7", size = 14424235, upload_time = "2024-10-01T21:46:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/35/55/c4d11bee1fdb0c4bd84b4e3562ff811a19b63266816870ae1f95567aa6e1/onnx-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:659b8232d627a5460d74fd3c96947ae83db6d03f035ac633e20cd69cfa029227", size = 14530453, upload_time = "2024-10-01T21:46:26.981Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a9/8d1b1d53aec70df53e0f57e9f9fcf47004276539e29230c3d5f1f50719ba/onnx-1.17.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:d6fc3a03fc0129b8b6ac03f03bc894431ffd77c7d79ec023d0afd667b4d35869", size = 16647991, upload-time = "2024-10-01T21:46:02.491Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e3/cc80110e5996ca61878f7b4c73c7a286cd88918ff35eacb60dc75ab11ef5/onnx-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a4b63d4e1d8ec3e2f069e7b798b2955810aa434f7361f01bc8ca08d69cce4", size = 15908949, upload-time = "2024-10-01T21:46:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2f/91092557ed478e323a2b4471e2081fdf88d1dd52ae988ceaf7db4e4506ff/onnx-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a183c6178be001bf398260e5ac2c927dc43e7746e8638d6c05c20e321f8c949", size = 16048190, upload-time = "2024-10-01T21:46:08.041Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/9ea23fc22d0bb853133f363e6248e31bcbc6c1c90543a3938c00412ac02a/onnx-1.17.0-cp311-cp311-win32.whl", hash = "sha256:081ec43a8b950171767d99075b6b92553901fa429d4bc5eb3ad66b36ef5dbe3a", size = 14424299, upload-time = "2024-10-01T21:46:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/51/a5/19b0dfcb567b62e7adf1a21b08b23224f0c2d13842aee4d0abc6f07f9cf5/onnx-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:95c03e38671785036bb704c30cd2e150825f6ab4763df3a4f1d249da48525957", size = 14529142, upload-time = "2024-10-01T21:46:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dd/c416a11a28847fafb0db1bf43381979a0f522eb9107b831058fde012dd56/onnx-1.17.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:0e906e6a83437de05f8139ea7eaf366bf287f44ae5cc44b2850a30e296421f2f", size = 16651271, upload-time = "2024-10-01T21:46:16.084Z" }, + { url = "https://files.pythonhosted.org/packages/f0/6c/f040652277f514ecd81b7251841f96caa5538365af7df07f86c6018cda2b/onnx-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d955ba2939878a520a97614bcf2e79c1df71b29203e8ced478fa78c9a9c63c2", size = 15907522, upload-time = "2024-10-01T21:46:18.574Z" }, + { url = "https://files.pythonhosted.org/packages/3d/7c/67f4952d1b56b3f74a154b97d0dd0630d525923b354db117d04823b8b49b/onnx-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3fb5cc4e2898ac5312a7dc03a65133dd2abf9a5e520e69afb880a7251ec97a", size = 16046307, upload-time = "2024-10-01T21:46:21.186Z" }, + { url = "https://files.pythonhosted.org/packages/ae/20/6da11042d2ab870dfb4ce4a6b52354d7651b6b4112038b6d2229ab9904c4/onnx-1.17.0-cp312-cp312-win32.whl", hash = "sha256:317870fca3349d19325a4b7d1b5628f6de3811e9710b1e3665c68b073d0e68d7", size = 14424235, upload-time = "2024-10-01T21:46:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/35/55/c4d11bee1fdb0c4bd84b4e3562ff811a19b63266816870ae1f95567aa6e1/onnx-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:659b8232d627a5460d74fd3c96947ae83db6d03f035ac633e20cd69cfa029227", size = 14530453, upload-time = "2024-10-01T21:46:26.981Z" }, ] [[package]] @@ -1240,14 +1240,14 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload_time = "2025-01-16T13:53:40.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload_time = "2025-01-16T13:52:57.015Z" }, - { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload_time = "2025-01-16T13:55:45.731Z" }, - { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload_time = "2025-01-16T13:51:59.625Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload_time = "2025-01-16T13:53:29.654Z" }, - { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload_time = "2025-01-16T13:52:49.048Z" }, - { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload_time = "2025-01-16T13:52:56.418Z" }, + { url = "https://files.pythonhosted.org/packages/dc/53/2c50afa0b1e05ecdb4603818e85f7d174e683d874ef63a6abe3ac92220c8/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca", size = 37326460, upload-time = "2025-01-16T13:52:57.015Z" }, + { url = "https://files.pythonhosted.org/packages/3b/43/68555327df94bb9b59a1fd645f63fafb0762515344d2046698762fc19d58/opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81", size = 56723330, upload-time = "2025-01-16T13:55:45.731Z" }, + { url = "https://files.pythonhosted.org/packages/45/be/1438ce43ebe65317344a87e4b150865c5585f4c0db880a34cdae5ac46881/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb", size = 29487060, upload-time = "2025-01-16T13:51:59.625Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5c/c139a7876099916879609372bfa513b7f1257f7f1a908b0bdc1c2328241b/opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b", size = 49969856, upload-time = "2025-01-16T13:53:29.654Z" }, + { url = "https://files.pythonhosted.org/packages/95/dd/ed1191c9dc91abcc9f752b499b7928aacabf10567bb2c2535944d848af18/opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b", size = 29324425, upload-time = "2025-01-16T13:52:49.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/8a/69176a64335aed183529207ba8bc3d329c2999d852b4f3818027203f50e6/opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca", size = 39402386, upload-time = "2025-01-16T13:52:56.418Z" }, ] [[package]] @@ -1420,9 +1420,9 @@ provides-extras = ["docs", "testing", "dev", "tools"] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload_time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload_time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] [[package]] @@ -1430,17 +1430,17 @@ name = "panda3d" version = "1.10.14" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/9a/31d07e3d7c1b40335e8418c540d63f4d33c571648ed8d69896ab778e65c3/panda3d-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54b8ef9fe3684960a2c7d47b0d63c0354c17bc516795e59db6c1e5bda8c16c1c", size = 67700752, upload_time = "2024-01-08T19:05:55.559Z" }, - { url = "https://files.pythonhosted.org/packages/61/05/fce327535d8907ac01f43813c980f30ea86d37db62c340847519ea2ab222/panda3d-1.10.14-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:93414675894b18eea8d27edc1bbd1dc719eae207d45ec263d47195504bc4705f", size = 118966179, upload_time = "2024-01-08T19:06:03.165Z" }, - { url = "https://files.pythonhosted.org/packages/8a/54/24e205231e7b1bced58ba9620fbec7114673d821fc7ad9ed1804cab556b4/panda3d-1.10.14-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:d1bc0d926f90c8fa14a1587fa9dbe5f89a4eda8c9684fa183a8eaa35fc8e891a", size = 55145295, upload_time = "2024-01-08T19:06:10.319Z" }, - { url = "https://files.pythonhosted.org/packages/06/d3/38e989822292935d7473d35117099f71481cc6b104cb2dd048cb8058a5a8/panda3d-1.10.14-cp311-cp311-win32.whl", hash = "sha256:1039340a4a7965fe4c3e904edb4fff691584df435a154fecccf534587cd07a34", size = 53137177, upload_time = "2024-01-08T19:06:15.901Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/b16c81661ed0d8ad62976004d81845baa321e53314e253ef0841155be770/panda3d-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:1ddf01040b9c5497fb8659e3c5ef793a26c869cfdfb1b75e6d04d6fba0c03b73", size = 64447666, upload_time = "2024-01-08T19:06:22.105Z" }, - { url = "https://files.pythonhosted.org/packages/5a/d4/90e98993b1a3f3c9fae83267f8c51186e676a8c1365c4180dfc65cd7ba62/panda3d-1.10.14-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1bfbcee77779f12ecce6a3d5a856e573b25d6343f8c4b107e814d9702e70a65d", size = 67839196, upload_time = "2024-01-08T19:01:00.417Z" }, - { url = "https://files.pythonhosted.org/packages/dc/e5/862821575073863ce49cc57b8349b47cb25ce11feae0e419b3d023ac1a69/panda3d-1.10.14-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:bc6540c5559d7e14a8992eff7de0157b7c42406b7ba221941ed224289496841c", size = 119271341, upload_time = "2024-01-08T19:01:09.455Z" }, - { url = "https://files.pythonhosted.org/packages/f4/20/f16d91805777825e530037177d9075c83da7384f12b778b133e3164a31f3/panda3d-1.10.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:143daab1ce6bedcba711ea3f6cab0ebe5082f22c5f43e7178fadd2dd01209da7", size = 47604077, upload_time = "2024-05-28T20:25:37.118Z" }, - { url = "https://files.pythonhosted.org/packages/11/69/806dcdbaee3e8deee1956abeea0d3d3e504315d2e9814de82a44809a8617/panda3d-1.10.14-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:3c4399a286a142de7ff86f9356d7e526bbbd38892d7f7d39fecb5c33064972bc", size = 55539594, upload_time = "2024-01-08T19:01:15.979Z" }, - { url = "https://files.pythonhosted.org/packages/ad/25/005de5e2b6d0acd546f8b3f2b547cd29e308cdd04a397f0ea68046e26571/panda3d-1.10.14-cp312-cp312-win32.whl", hash = "sha256:e92e0dd907e2af33085a2c31ca2263dc8023b1b7bc70ce1b9fbc84631e130e51", size = 53479813, upload_time = "2024-01-08T19:01:20.923Z" }, - { url = "https://files.pythonhosted.org/packages/74/bb/cb57563855da994614a33f57bd5691fbcd69f12e5ccddd30d387d0be287f/panda3d-1.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:a5f2defd822d38848f8ae1956115adcb6cc7f464f03a67e73681cc72df125ef4", size = 64893222, upload_time = "2024-01-08T19:01:26.347Z" }, + { url = "https://files.pythonhosted.org/packages/f5/9a/31d07e3d7c1b40335e8418c540d63f4d33c571648ed8d69896ab778e65c3/panda3d-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54b8ef9fe3684960a2c7d47b0d63c0354c17bc516795e59db6c1e5bda8c16c1c", size = 67700752, upload-time = "2024-01-08T19:05:55.559Z" }, + { url = "https://files.pythonhosted.org/packages/61/05/fce327535d8907ac01f43813c980f30ea86d37db62c340847519ea2ab222/panda3d-1.10.14-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:93414675894b18eea8d27edc1bbd1dc719eae207d45ec263d47195504bc4705f", size = 118966179, upload-time = "2024-01-08T19:06:03.165Z" }, + { url = "https://files.pythonhosted.org/packages/8a/54/24e205231e7b1bced58ba9620fbec7114673d821fc7ad9ed1804cab556b4/panda3d-1.10.14-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:d1bc0d926f90c8fa14a1587fa9dbe5f89a4eda8c9684fa183a8eaa35fc8e891a", size = 55145295, upload-time = "2024-01-08T19:06:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/06/d3/38e989822292935d7473d35117099f71481cc6b104cb2dd048cb8058a5a8/panda3d-1.10.14-cp311-cp311-win32.whl", hash = "sha256:1039340a4a7965fe4c3e904edb4fff691584df435a154fecccf534587cd07a34", size = 53137177, upload-time = "2024-01-08T19:06:15.901Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/b16c81661ed0d8ad62976004d81845baa321e53314e253ef0841155be770/panda3d-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:1ddf01040b9c5497fb8659e3c5ef793a26c869cfdfb1b75e6d04d6fba0c03b73", size = 64447666, upload-time = "2024-01-08T19:06:22.105Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d4/90e98993b1a3f3c9fae83267f8c51186e676a8c1365c4180dfc65cd7ba62/panda3d-1.10.14-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1bfbcee77779f12ecce6a3d5a856e573b25d6343f8c4b107e814d9702e70a65d", size = 67839196, upload-time = "2024-01-08T19:01:00.417Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e5/862821575073863ce49cc57b8349b47cb25ce11feae0e419b3d023ac1a69/panda3d-1.10.14-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:bc6540c5559d7e14a8992eff7de0157b7c42406b7ba221941ed224289496841c", size = 119271341, upload-time = "2024-01-08T19:01:09.455Z" }, + { url = "https://files.pythonhosted.org/packages/f4/20/f16d91805777825e530037177d9075c83da7384f12b778b133e3164a31f3/panda3d-1.10.14-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:143daab1ce6bedcba711ea3f6cab0ebe5082f22c5f43e7178fadd2dd01209da7", size = 47604077, upload-time = "2024-05-28T20:25:37.118Z" }, + { url = "https://files.pythonhosted.org/packages/11/69/806dcdbaee3e8deee1956abeea0d3d3e504315d2e9814de82a44809a8617/panda3d-1.10.14-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:3c4399a286a142de7ff86f9356d7e526bbbd38892d7f7d39fecb5c33064972bc", size = 55539594, upload-time = "2024-01-08T19:01:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/ad/25/005de5e2b6d0acd546f8b3f2b547cd29e308cdd04a397f0ea68046e26571/panda3d-1.10.14-cp312-cp312-win32.whl", hash = "sha256:e92e0dd907e2af33085a2c31ca2263dc8023b1b7bc70ce1b9fbc84631e130e51", size = 53479813, upload-time = "2024-01-08T19:01:20.923Z" }, + { url = "https://files.pythonhosted.org/packages/74/bb/cb57563855da994614a33f57bd5691fbcd69f12e5ccddd30d387d0be287f/panda3d-1.10.14-cp312-cp312-win_amd64.whl", hash = "sha256:a5f2defd822d38848f8ae1956115adcb6cc7f464f03a67e73681cc72df125ef4", size = 64893222, upload-time = "2024-01-08T19:01:26.347Z" }, ] [[package]] @@ -1451,9 +1451,9 @@ dependencies = [ { name = "panda3d" }, { name = "panda3d-simplepbr" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload_time = "2021-05-21T05:46:32.738Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/94/98ed1f81ca0f5daf6de80533805cc1e98ac162abe4e3e1d382caa7ba5c3c/panda3d_gltf-0.13-py3-none-any.whl", hash = "sha256:02d1a980f447bb1895ff4b48c667f289ba78f07a28ef308d8839b665a621efe2", size = 25568, upload_time = "2021-05-21T05:46:31.28Z" }, + { url = "https://files.pythonhosted.org/packages/70/94/98ed1f81ca0f5daf6de80533805cc1e98ac162abe4e3e1d382caa7ba5c3c/panda3d_gltf-0.13-py3-none-any.whl", hash = "sha256:02d1a980f447bb1895ff4b48c667f289ba78f07a28ef308d8839b665a621efe2", size = 25568, upload-time = "2021-05-21T05:46:31.28Z" }, ] [[package]] @@ -1464,82 +1464,82 @@ dependencies = [ { name = "panda3d" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload_time = "2025-03-30T16:57:41.087Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/5d/3744c6550dddf933785a37cdd4a9921fe13284e6d115b5a2637fe390f158/panda3d_simplepbr-0.13.1-py3-none-any.whl", hash = "sha256:cda41cb57cff035b851646956cfbdcc408bee42511dabd4f2d7bd4fbf48c57a9", size = 2457097, upload_time = "2025-03-30T16:57:39.729Z" }, + { url = "https://files.pythonhosted.org/packages/11/5d/3744c6550dddf933785a37cdd4a9921fe13284e6d115b5a2637fe390f158/panda3d_simplepbr-0.13.1-py3-none-any.whl", hash = "sha256:cda41cb57cff035b851646956cfbdcc408bee42511dabd4f2d7bd4fbf48c57a9", size = 2457097, upload-time = "2025-03-30T16:57:39.729Z" }, ] [[package]] name = "parameterized" version = "0.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c6/23/2288f308d238b4f261c039cafcd650435d624de97c6ffc903f06ea8af50f/parameterized-0.8.1.tar.gz", hash = "sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c", size = 23936, upload_time = "2021-01-09T20:35:18.235Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/23/2288f308d238b4f261c039cafcd650435d624de97c6ffc903f06ea8af50f/parameterized-0.8.1.tar.gz", hash = "sha256:41bbff37d6186430f77f900d777e5bb6a24928a1c46fb1de692f8b52b8833b5c", size = 23936, upload-time = "2021-01-09T20:35:18.235Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/13/fe468c8c7400a8eca204e6e160a29bf7dcd45a76e20f1c030f3eaa690d93/parameterized-0.8.1-py2.py3-none-any.whl", hash = "sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9", size = 26354, upload_time = "2021-01-09T20:35:16.307Z" }, + { url = "https://files.pythonhosted.org/packages/31/13/fe468c8c7400a8eca204e6e160a29bf7dcd45a76e20f1c030f3eaa690d93/parameterized-0.8.1-py2.py3-none-any.whl", hash = "sha256:9cbb0b69a03e8695d68b3399a8a5825200976536fe1cb79db60ed6a4c8c9efe9", size = 26354, upload-time = "2021-01-09T20:35:16.307Z" }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload_time = "2023-12-10T22:30:45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload_time = "2023-12-10T22:30:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] [[package]] name = "pillow" version = "11.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload_time = "2025-04-12T17:50:03.289Z" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cb/bb5c01fcd2a69335b86c22142b2bccfc3464087efb7fd382eee5ffc7fdf7/pillow-11.2.1.tar.gz", hash = "sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6", size = 47026707, upload-time = "2025-04-12T17:50:03.289Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450, upload_time = "2025-04-12T17:47:37.135Z" }, - { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550, upload_time = "2025-04-12T17:47:39.345Z" }, - { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018, upload_time = "2025-04-12T17:47:41.128Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006, upload_time = "2025-04-12T17:47:42.912Z" }, - { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773, upload_time = "2025-04-12T17:47:44.611Z" }, - { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069, upload_time = "2025-04-12T17:47:46.46Z" }, - { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460, upload_time = "2025-04-12T17:47:49.255Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304, upload_time = "2025-04-12T17:47:51.067Z" }, - { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809, upload_time = "2025-04-12T17:47:54.425Z" }, - { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338, upload_time = "2025-04-12T17:47:56.535Z" }, - { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918, upload_time = "2025-04-12T17:47:58.217Z" }, - { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185, upload_time = "2025-04-12T17:48:00.417Z" }, - { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306, upload_time = "2025-04-12T17:48:02.391Z" }, - { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121, upload_time = "2025-04-12T17:48:04.554Z" }, - { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707, upload_time = "2025-04-12T17:48:06.831Z" }, - { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921, upload_time = "2025-04-12T17:48:09.229Z" }, - { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523, upload_time = "2025-04-12T17:48:11.631Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836, upload_time = "2025-04-12T17:48:13.592Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390, upload_time = "2025-04-12T17:48:15.938Z" }, - { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309, upload_time = "2025-04-12T17:48:17.885Z" }, - { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768, upload_time = "2025-04-12T17:48:19.655Z" }, - { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087, upload_time = "2025-04-12T17:48:21.991Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734, upload_time = "2025-04-12T17:49:46.789Z" }, - { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841, upload_time = "2025-04-12T17:49:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470, upload_time = "2025-04-12T17:49:50.831Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013, upload_time = "2025-04-12T17:49:53.278Z" }, - { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165, upload_time = "2025-04-12T17:49:55.164Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586, upload_time = "2025-04-12T17:49:57.171Z" }, - { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751, upload_time = "2025-04-12T17:49:59.628Z" }, + { url = "https://files.pythonhosted.org/packages/68/08/3fbf4b98924c73037a8e8b4c2c774784805e0fb4ebca6c5bb60795c40125/pillow-11.2.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70", size = 3198450, upload-time = "2025-04-12T17:47:37.135Z" }, + { url = "https://files.pythonhosted.org/packages/84/92/6505b1af3d2849d5e714fc75ba9e69b7255c05ee42383a35a4d58f576b16/pillow-11.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf", size = 3030550, upload-time = "2025-04-12T17:47:39.345Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8c/ac2f99d2a70ff966bc7eb13dacacfaab57c0549b2ffb351b6537c7840b12/pillow-11.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7", size = 4415018, upload-time = "2025-04-12T17:47:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e3/0a58b5d838687f40891fff9cbaf8669f90c96b64dc8f91f87894413856c6/pillow-11.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8", size = 4498006, upload-time = "2025-04-12T17:47:42.912Z" }, + { url = "https://files.pythonhosted.org/packages/21/f5/6ba14718135f08fbfa33308efe027dd02b781d3f1d5c471444a395933aac/pillow-11.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600", size = 4517773, upload-time = "2025-04-12T17:47:44.611Z" }, + { url = "https://files.pythonhosted.org/packages/20/f2/805ad600fc59ebe4f1ba6129cd3a75fb0da126975c8579b8f57abeb61e80/pillow-11.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788", size = 4607069, upload-time = "2025-04-12T17:47:46.46Z" }, + { url = "https://files.pythonhosted.org/packages/71/6b/4ef8a288b4bb2e0180cba13ca0a519fa27aa982875882392b65131401099/pillow-11.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e", size = 4583460, upload-time = "2025-04-12T17:47:49.255Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/f29c705a09cbc9e2a456590816e5c234382ae5d32584f451c3eb41a62062/pillow-11.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e", size = 4661304, upload-time = "2025-04-12T17:47:51.067Z" }, + { url = "https://files.pythonhosted.org/packages/6e/1a/c8217b6f2f73794a5e219fbad087701f412337ae6dbb956db37d69a9bc43/pillow-11.2.1-cp311-cp311-win32.whl", hash = "sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6", size = 2331809, upload-time = "2025-04-12T17:47:54.425Z" }, + { url = "https://files.pythonhosted.org/packages/e2/72/25a8f40170dc262e86e90f37cb72cb3de5e307f75bf4b02535a61afcd519/pillow-11.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193", size = 2676338, upload-time = "2025-04-12T17:47:56.535Z" }, + { url = "https://files.pythonhosted.org/packages/06/9e/76825e39efee61efea258b479391ca77d64dbd9e5804e4ad0fa453b4ba55/pillow-11.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7", size = 2414918, upload-time = "2025-04-12T17:47:58.217Z" }, + { url = "https://files.pythonhosted.org/packages/c7/40/052610b15a1b8961f52537cc8326ca6a881408bc2bdad0d852edeb6ed33b/pillow-11.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f", size = 3190185, upload-time = "2025-04-12T17:48:00.417Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7e/b86dbd35a5f938632093dc40d1682874c33dcfe832558fc80ca56bfcb774/pillow-11.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b", size = 3030306, upload-time = "2025-04-12T17:48:02.391Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5c/467a161f9ed53e5eab51a42923c33051bf8d1a2af4626ac04f5166e58e0c/pillow-11.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d", size = 4416121, upload-time = "2025-04-12T17:48:04.554Z" }, + { url = "https://files.pythonhosted.org/packages/62/73/972b7742e38ae0e2ac76ab137ca6005dcf877480da0d9d61d93b613065b4/pillow-11.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4", size = 4501707, upload-time = "2025-04-12T17:48:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/e4/3a/427e4cb0b9e177efbc1a84798ed20498c4f233abde003c06d2650a6d60cb/pillow-11.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d", size = 4522921, upload-time = "2025-04-12T17:48:09.229Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7c/d8b1330458e4d2f3f45d9508796d7caf0c0d3764c00c823d10f6f1a3b76d/pillow-11.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4", size = 4612523, upload-time = "2025-04-12T17:48:11.631Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2f/65738384e0b1acf451de5a573d8153fe84103772d139e1e0bdf1596be2ea/pillow-11.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443", size = 4587836, upload-time = "2025-04-12T17:48:13.592Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c5/e795c9f2ddf3debb2dedd0df889f2fe4b053308bb59a3cc02a0cd144d641/pillow-11.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c", size = 4669390, upload-time = "2025-04-12T17:48:15.938Z" }, + { url = "https://files.pythonhosted.org/packages/96/ae/ca0099a3995976a9fce2f423166f7bff9b12244afdc7520f6ed38911539a/pillow-11.2.1-cp312-cp312-win32.whl", hash = "sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3", size = 2332309, upload-time = "2025-04-12T17:48:17.885Z" }, + { url = "https://files.pythonhosted.org/packages/7c/18/24bff2ad716257fc03da964c5e8f05d9790a779a8895d6566e493ccf0189/pillow-11.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941", size = 2676768, upload-time = "2025-04-12T17:48:19.655Z" }, + { url = "https://files.pythonhosted.org/packages/da/bb/e8d656c9543276517ee40184aaa39dcb41e683bca121022f9323ae11b39d/pillow-11.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb", size = 2415087, upload-time = "2025-04-12T17:48:21.991Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ad/2613c04633c7257d9481ab21d6b5364b59fc5d75faafd7cb8693523945a3/pillow-11.2.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed", size = 3181734, upload-time = "2025-04-12T17:49:46.789Z" }, + { url = "https://files.pythonhosted.org/packages/a4/fd/dcdda4471ed667de57bb5405bb42d751e6cfdd4011a12c248b455c778e03/pillow-11.2.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c", size = 2999841, upload-time = "2025-04-12T17:49:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/ac/89/8a2536e95e77432833f0db6fd72a8d310c8e4272a04461fb833eb021bf94/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd", size = 3437470, upload-time = "2025-04-12T17:49:50.831Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8f/abd47b73c60712f88e9eda32baced7bfc3e9bd6a7619bb64b93acff28c3e/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076", size = 3460013, upload-time = "2025-04-12T17:49:53.278Z" }, + { url = "https://files.pythonhosted.org/packages/f6/20/5c0a0aa83b213b7a07ec01e71a3d6ea2cf4ad1d2c686cc0168173b6089e7/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b", size = 3527165, upload-time = "2025-04-12T17:49:55.164Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/2abab98a72202d91146abc839e10c14f7cf36166f12838ea0c4db3ca6ecb/pillow-11.2.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f", size = 3571586, upload-time = "2025-04-12T17:49:57.171Z" }, + { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751, upload-time = "2025-04-12T17:49:59.628Z" }, ] [[package]] name = "platformdirs" version = "4.3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload_time = "2025-03-19T20:36:10.989Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload-time = "2025-03-19T20:36:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload_time = "2025-03-19T20:36:09.038Z" }, + { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload-time = "2025-03-19T20:36:09.038Z" }, ] [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload_time = "2024-04-20T21:34:42.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload_time = "2024-04-20T21:34:40.434Z" }, + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] [[package]] @@ -1549,123 +1549,123 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/7d/3299241a753c738d114600c360d754550b28c285281dc6a5132c4ccfae65/pre_commit_hooks-5.0.0.tar.gz", hash = "sha256:10626959a9eaf602fbfc22bc61b6e75801436f82326bfcee82bb1f2fc4bc646e", size = 29747, upload_time = "2024-10-05T18:43:11.225Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/7d/3299241a753c738d114600c360d754550b28c285281dc6a5132c4ccfae65/pre_commit_hooks-5.0.0.tar.gz", hash = "sha256:10626959a9eaf602fbfc22bc61b6e75801436f82326bfcee82bb1f2fc4bc646e", size = 29747, upload-time = "2024-10-05T18:43:11.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/29/db1d855a661c02dbde5cab3057969133fcc62e7a0c6393e48fe9d0e81679/pre_commit_hooks-5.0.0-py2.py3-none-any.whl", hash = "sha256:8d71cfb582c5c314a5498d94e0104b6567a8b93fb35903ea845c491f4e290a7a", size = 41245, upload_time = "2024-10-05T18:43:09.901Z" }, + { url = "https://files.pythonhosted.org/packages/1e/29/db1d855a661c02dbde5cab3057969133fcc62e7a0c6393e48fe9d0e81679/pre_commit_hooks-5.0.0-py2.py3-none-any.whl", hash = "sha256:8d71cfb582c5c314a5498d94e0104b6567a8b93fb35903ea845c491f4e290a7a", size = 41245, upload-time = "2024-10-05T18:43:09.901Z" }, ] [[package]] name = "progressbar" version = "2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/a6/b8e451f6cff1c99b4747a2f7235aa904d2d49e8e1464e0b798272aa84358/progressbar-2.5.tar.gz", hash = "sha256:5d81cb529da2e223b53962afd6c8ca0f05c6670e40309a7219eacc36af9b6c63", size = 10046, upload_time = "2018-06-29T02:32:00.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/a6/b8e451f6cff1c99b4747a2f7235aa904d2d49e8e1464e0b798272aa84358/progressbar-2.5.tar.gz", hash = "sha256:5d81cb529da2e223b53962afd6c8ca0f05c6670e40309a7219eacc36af9b6c63", size = 10046, upload-time = "2018-06-29T02:32:00.222Z" } [[package]] name = "propcache" version = "0.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651, upload_time = "2025-03-26T03:06:12.05Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651, upload-time = "2025-03-26T03:06:12.05Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243, upload_time = "2025-03-26T03:04:01.912Z" }, - { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503, upload_time = "2025-03-26T03:04:03.704Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934, upload_time = "2025-03-26T03:04:05.257Z" }, - { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633, upload_time = "2025-03-26T03:04:07.044Z" }, - { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124, upload_time = "2025-03-26T03:04:08.676Z" }, - { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283, upload_time = "2025-03-26T03:04:10.172Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498, upload_time = "2025-03-26T03:04:11.616Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486, upload_time = "2025-03-26T03:04:13.102Z" }, - { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675, upload_time = "2025-03-26T03:04:14.658Z" }, - { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727, upload_time = "2025-03-26T03:04:16.207Z" }, - { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878, upload_time = "2025-03-26T03:04:18.11Z" }, - { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558, upload_time = "2025-03-26T03:04:19.562Z" }, - { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754, upload_time = "2025-03-26T03:04:21.065Z" }, - { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088, upload_time = "2025-03-26T03:04:22.718Z" }, - { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859, upload_time = "2025-03-26T03:04:24.039Z" }, - { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153, upload_time = "2025-03-26T03:04:25.211Z" }, - { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430, upload_time = "2025-03-26T03:04:26.436Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637, upload_time = "2025-03-26T03:04:27.932Z" }, - { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123, upload_time = "2025-03-26T03:04:30.659Z" }, - { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031, upload_time = "2025-03-26T03:04:31.977Z" }, - { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100, upload_time = "2025-03-26T03:04:33.45Z" }, - { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170, upload_time = "2025-03-26T03:04:35.542Z" }, - { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000, upload_time = "2025-03-26T03:04:37.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262, upload_time = "2025-03-26T03:04:39.532Z" }, - { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772, upload_time = "2025-03-26T03:04:41.109Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133, upload_time = "2025-03-26T03:04:42.544Z" }, - { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741, upload_time = "2025-03-26T03:04:44.06Z" }, - { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047, upload_time = "2025-03-26T03:04:45.983Z" }, - { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467, upload_time = "2025-03-26T03:04:47.699Z" }, - { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022, upload_time = "2025-03-26T03:04:49.195Z" }, - { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647, upload_time = "2025-03-26T03:04:50.595Z" }, - { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784, upload_time = "2025-03-26T03:04:51.791Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376, upload_time = "2025-03-26T03:06:10.5Z" }, + { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243, upload-time = "2025-03-26T03:04:01.912Z" }, + { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503, upload-time = "2025-03-26T03:04:03.704Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934, upload-time = "2025-03-26T03:04:05.257Z" }, + { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633, upload-time = "2025-03-26T03:04:07.044Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124, upload-time = "2025-03-26T03:04:08.676Z" }, + { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283, upload-time = "2025-03-26T03:04:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498, upload-time = "2025-03-26T03:04:11.616Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486, upload-time = "2025-03-26T03:04:13.102Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675, upload-time = "2025-03-26T03:04:14.658Z" }, + { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727, upload-time = "2025-03-26T03:04:16.207Z" }, + { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878, upload-time = "2025-03-26T03:04:18.11Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558, upload-time = "2025-03-26T03:04:19.562Z" }, + { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754, upload-time = "2025-03-26T03:04:21.065Z" }, + { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088, upload-time = "2025-03-26T03:04:22.718Z" }, + { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859, upload-time = "2025-03-26T03:04:24.039Z" }, + { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153, upload-time = "2025-03-26T03:04:25.211Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430, upload-time = "2025-03-26T03:04:26.436Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637, upload-time = "2025-03-26T03:04:27.932Z" }, + { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123, upload-time = "2025-03-26T03:04:30.659Z" }, + { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031, upload-time = "2025-03-26T03:04:31.977Z" }, + { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100, upload-time = "2025-03-26T03:04:33.45Z" }, + { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170, upload-time = "2025-03-26T03:04:35.542Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000, upload-time = "2025-03-26T03:04:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262, upload-time = "2025-03-26T03:04:39.532Z" }, + { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772, upload-time = "2025-03-26T03:04:41.109Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133, upload-time = "2025-03-26T03:04:42.544Z" }, + { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741, upload-time = "2025-03-26T03:04:44.06Z" }, + { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047, upload-time = "2025-03-26T03:04:45.983Z" }, + { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467, upload-time = "2025-03-26T03:04:47.699Z" }, + { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022, upload-time = "2025-03-26T03:04:49.195Z" }, + { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647, upload-time = "2025-03-26T03:04:50.595Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784, upload-time = "2025-03-26T03:04:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376, upload-time = "2025-03-26T03:06:10.5Z" }, ] [[package]] name = "protobuf" version = "6.30.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315, upload_time = "2025-03-26T19:12:57.394Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315, upload-time = "2025-03-26T19:12:57.394Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148, upload_time = "2025-03-26T19:12:41.359Z" }, - { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003, upload_time = "2025-03-26T19:12:44.156Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579, upload_time = "2025-03-26T19:12:45.447Z" }, - { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319, upload_time = "2025-03-26T19:12:46.999Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212, upload_time = "2025-03-26T19:12:48.458Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062, upload_time = "2025-03-26T19:12:55.892Z" }, + { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148, upload-time = "2025-03-26T19:12:41.359Z" }, + { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003, upload-time = "2025-03-26T19:12:44.156Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579, upload-time = "2025-03-26T19:12:45.447Z" }, + { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319, upload-time = "2025-03-26T19:12:46.999Z" }, + { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212, upload-time = "2025-03-26T19:12:48.458Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062, upload-time = "2025-03-26T19:12:55.892Z" }, ] [[package]] name = "psutil" version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload_time = "2025-02-13T21:54:07.946Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload_time = "2025-02-13T21:54:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload_time = "2025-02-13T21:54:16.07Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload_time = "2025-02-13T21:54:18.662Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload_time = "2025-02-13T21:54:21.811Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload_time = "2025-02-13T21:54:24.68Z" }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload_time = "2025-02-13T21:54:34.31Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload_time = "2025-02-13T21:54:37.486Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, ] [[package]] name = "pyarrow" version = "20.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187, upload_time = "2025-04-27T12:34:23.264Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187, upload-time = "2025-04-27T12:34:23.264Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/a2/b7930824181ceadd0c63c1042d01fa4ef63eee233934826a7a2a9af6e463/pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0", size = 30856035, upload_time = "2025-04-27T12:28:40.78Z" }, - { url = "https://files.pythonhosted.org/packages/9b/18/c765770227d7f5bdfa8a69f64b49194352325c66a5c3bb5e332dfd5867d9/pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb", size = 32309552, upload_time = "2025-04-27T12:28:47.051Z" }, - { url = "https://files.pythonhosted.org/packages/44/fb/dfb2dfdd3e488bb14f822d7335653092dde150cffc2da97de6e7500681f9/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232", size = 41334704, upload_time = "2025-04-27T12:28:55.064Z" }, - { url = "https://files.pythonhosted.org/packages/58/0d/08a95878d38808051a953e887332d4a76bc06c6ee04351918ee1155407eb/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f", size = 42399836, upload_time = "2025-04-27T12:29:02.13Z" }, - { url = "https://files.pythonhosted.org/packages/f3/cd/efa271234dfe38f0271561086eedcad7bc0f2ddd1efba423916ff0883684/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab", size = 40711789, upload_time = "2025-04-27T12:29:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/46/1f/7f02009bc7fc8955c391defee5348f510e589a020e4b40ca05edcb847854/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62", size = 42301124, upload_time = "2025-04-27T12:29:17.187Z" }, - { url = "https://files.pythonhosted.org/packages/4f/92/692c562be4504c262089e86757a9048739fe1acb4024f92d39615e7bab3f/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c", size = 42916060, upload_time = "2025-04-27T12:29:24.253Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ec/9f5c7e7c828d8e0a3c7ef50ee62eca38a7de2fa6eb1b8fa43685c9414fef/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3", size = 44547640, upload_time = "2025-04-27T12:29:32.782Z" }, - { url = "https://files.pythonhosted.org/packages/54/96/46613131b4727f10fd2ffa6d0d6f02efcc09a0e7374eff3b5771548aa95b/pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc", size = 25781491, upload_time = "2025-04-27T12:29:38.464Z" }, - { url = "https://files.pythonhosted.org/packages/a1/d6/0c10e0d54f6c13eb464ee9b67a68b8c71bcf2f67760ef5b6fbcddd2ab05f/pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba", size = 30815067, upload_time = "2025-04-27T12:29:44.384Z" }, - { url = "https://files.pythonhosted.org/packages/7e/e2/04e9874abe4094a06fd8b0cbb0f1312d8dd7d707f144c2ec1e5e8f452ffa/pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781", size = 32297128, upload_time = "2025-04-27T12:29:52.038Z" }, - { url = "https://files.pythonhosted.org/packages/31/fd/c565e5dcc906a3b471a83273039cb75cb79aad4a2d4a12f76cc5ae90a4b8/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199", size = 41334890, upload_time = "2025-04-27T12:29:59.452Z" }, - { url = "https://files.pythonhosted.org/packages/af/a9/3bdd799e2c9b20c1ea6dc6fa8e83f29480a97711cf806e823f808c2316ac/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd", size = 42421775, upload_time = "2025-04-27T12:30:06.875Z" }, - { url = "https://files.pythonhosted.org/packages/10/f7/da98ccd86354c332f593218101ae56568d5dcedb460e342000bd89c49cc1/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28", size = 40687231, upload_time = "2025-04-27T12:30:13.954Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1b/2168d6050e52ff1e6cefc61d600723870bf569cbf41d13db939c8cf97a16/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8", size = 42295639, upload_time = "2025-04-27T12:30:21.949Z" }, - { url = "https://files.pythonhosted.org/packages/b2/66/2d976c0c7158fd25591c8ca55aee026e6d5745a021915a1835578707feb3/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e", size = 42908549, upload_time = "2025-04-27T12:30:29.551Z" }, - { url = "https://files.pythonhosted.org/packages/31/a9/dfb999c2fc6911201dcbf348247f9cc382a8990f9ab45c12eabfd7243a38/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a", size = 44557216, upload_time = "2025-04-27T12:30:36.977Z" }, - { url = "https://files.pythonhosted.org/packages/a0/8e/9adee63dfa3911be2382fb4d92e4b2e7d82610f9d9f668493bebaa2af50f/pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b", size = 25660496, upload_time = "2025-04-27T12:30:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/b7930824181ceadd0c63c1042d01fa4ef63eee233934826a7a2a9af6e463/pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0", size = 30856035, upload-time = "2025-04-27T12:28:40.78Z" }, + { url = "https://files.pythonhosted.org/packages/9b/18/c765770227d7f5bdfa8a69f64b49194352325c66a5c3bb5e332dfd5867d9/pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb", size = 32309552, upload-time = "2025-04-27T12:28:47.051Z" }, + { url = "https://files.pythonhosted.org/packages/44/fb/dfb2dfdd3e488bb14f822d7335653092dde150cffc2da97de6e7500681f9/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232", size = 41334704, upload-time = "2025-04-27T12:28:55.064Z" }, + { url = "https://files.pythonhosted.org/packages/58/0d/08a95878d38808051a953e887332d4a76bc06c6ee04351918ee1155407eb/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f", size = 42399836, upload-time = "2025-04-27T12:29:02.13Z" }, + { url = "https://files.pythonhosted.org/packages/f3/cd/efa271234dfe38f0271561086eedcad7bc0f2ddd1efba423916ff0883684/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab", size = 40711789, upload-time = "2025-04-27T12:29:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/46/1f/7f02009bc7fc8955c391defee5348f510e589a020e4b40ca05edcb847854/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62", size = 42301124, upload-time = "2025-04-27T12:29:17.187Z" }, + { url = "https://files.pythonhosted.org/packages/4f/92/692c562be4504c262089e86757a9048739fe1acb4024f92d39615e7bab3f/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c", size = 42916060, upload-time = "2025-04-27T12:29:24.253Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ec/9f5c7e7c828d8e0a3c7ef50ee62eca38a7de2fa6eb1b8fa43685c9414fef/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3", size = 44547640, upload-time = "2025-04-27T12:29:32.782Z" }, + { url = "https://files.pythonhosted.org/packages/54/96/46613131b4727f10fd2ffa6d0d6f02efcc09a0e7374eff3b5771548aa95b/pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc", size = 25781491, upload-time = "2025-04-27T12:29:38.464Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d6/0c10e0d54f6c13eb464ee9b67a68b8c71bcf2f67760ef5b6fbcddd2ab05f/pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba", size = 30815067, upload-time = "2025-04-27T12:29:44.384Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e2/04e9874abe4094a06fd8b0cbb0f1312d8dd7d707f144c2ec1e5e8f452ffa/pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781", size = 32297128, upload-time = "2025-04-27T12:29:52.038Z" }, + { url = "https://files.pythonhosted.org/packages/31/fd/c565e5dcc906a3b471a83273039cb75cb79aad4a2d4a12f76cc5ae90a4b8/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199", size = 41334890, upload-time = "2025-04-27T12:29:59.452Z" }, + { url = "https://files.pythonhosted.org/packages/af/a9/3bdd799e2c9b20c1ea6dc6fa8e83f29480a97711cf806e823f808c2316ac/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd", size = 42421775, upload-time = "2025-04-27T12:30:06.875Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/da98ccd86354c332f593218101ae56568d5dcedb460e342000bd89c49cc1/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28", size = 40687231, upload-time = "2025-04-27T12:30:13.954Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1b/2168d6050e52ff1e6cefc61d600723870bf569cbf41d13db939c8cf97a16/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8", size = 42295639, upload-time = "2025-04-27T12:30:21.949Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/2d976c0c7158fd25591c8ca55aee026e6d5745a021915a1835578707feb3/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e", size = 42908549, upload-time = "2025-04-27T12:30:29.551Z" }, + { url = "https://files.pythonhosted.org/packages/31/a9/dfb999c2fc6911201dcbf348247f9cc382a8990f9ab45c12eabfd7243a38/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a", size = 44557216, upload-time = "2025-04-27T12:30:36.977Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8e/9adee63dfa3911be2382fb4d92e4b2e7d82610f9d9f668493bebaa2af50f/pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b", size = 25660496, upload-time = "2025-04-27T12:30:42.809Z" }, ] [[package]] name = "pyaudio" version = "0.2.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/1d/8878c7752febb0f6716a7e1a52cb92ac98871c5aa522cba181878091607c/PyAudio-0.2.14.tar.gz", hash = "sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87", size = 47066, upload_time = "2023-11-07T07:11:48.806Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/1d/8878c7752febb0f6716a7e1a52cb92ac98871c5aa522cba181878091607c/PyAudio-0.2.14.tar.gz", hash = "sha256:78dfff3879b4994d1f4fc6485646a57755c6ee3c19647a491f790a0895bd2f87", size = 47066, upload-time = "2023-11-07T07:11:48.806Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/f0/b0eab89eafa70a86b7b566a4df2f94c7880a2d483aa8de1c77d335335b5b/PyAudio-0.2.14-cp311-cp311-win32.whl", hash = "sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289", size = 144624, upload_time = "2023-11-07T07:11:36.94Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f043c854aad450a76e476b0cf9cda1956419e1dacf1062eb9df3c0055abe/PyAudio-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903", size = 164070, upload_time = "2023-11-07T07:11:38.579Z" }, - { url = "https://files.pythonhosted.org/packages/8d/45/8d2b76e8f6db783f9326c1305f3f816d4a12c8eda5edc6a2e1d03c097c3b/PyAudio-0.2.14-cp312-cp312-win32.whl", hash = "sha256:5fce4bcdd2e0e8c063d835dbe2860dac46437506af509353c7f8114d4bacbd5b", size = 144750, upload_time = "2023-11-07T07:11:40.142Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/d25812e5f79f06285767ec607b39149d02aa3b31d50c2269768f48768930/PyAudio-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3", size = 164126, upload_time = "2023-11-07T07:11:41.539Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f0/b0eab89eafa70a86b7b566a4df2f94c7880a2d483aa8de1c77d335335b5b/PyAudio-0.2.14-cp311-cp311-win32.whl", hash = "sha256:506b32a595f8693811682ab4b127602d404df7dfc453b499c91a80d0f7bad289", size = 144624, upload-time = "2023-11-07T07:11:36.94Z" }, + { url = "https://files.pythonhosted.org/packages/82/d8/f043c854aad450a76e476b0cf9cda1956419e1dacf1062eb9df3c0055abe/PyAudio-0.2.14-cp311-cp311-win_amd64.whl", hash = "sha256:bbeb01d36a2f472ae5ee5e1451cacc42112986abe622f735bb870a5db77cf903", size = 164070, upload-time = "2023-11-07T07:11:38.579Z" }, + { url = "https://files.pythonhosted.org/packages/8d/45/8d2b76e8f6db783f9326c1305f3f816d4a12c8eda5edc6a2e1d03c097c3b/PyAudio-0.2.14-cp312-cp312-win32.whl", hash = "sha256:5fce4bcdd2e0e8c063d835dbe2860dac46437506af509353c7f8114d4bacbd5b", size = 144750, upload-time = "2023-11-07T07:11:40.142Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/d25812e5f79f06285767ec607b39149d02aa3b31d50c2269768f48768930/PyAudio-0.2.14-cp312-cp312-win_amd64.whl", hash = "sha256:12f2f1ba04e06ff95d80700a78967897a489c05e093e3bffa05a84ed9c0a7fa3", size = 164126, upload-time = "2023-11-07T07:11:41.539Z" }, ] [[package]] @@ -1682,69 +1682,69 @@ dependencies = [ { name = "python3-xlib", marker = "sys_platform == 'linux'" }, { name = "pytweening" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236, upload_time = "2023-05-24T20:11:32.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ff/cdae0a8c2118a0de74b6cf4cbcdcaf8fd25857e6c3f205ce4b1794b27814/PyAutoGUI-0.9.54.tar.gz", hash = "sha256:dd1d29e8fd118941cb193f74df57e5c6ff8e9253b99c7b04f39cfc69f3ae04b2", size = 61236, upload-time = "2023-05-24T20:11:32.972Z" } [[package]] name = "pycapnp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848, upload_time = "2024-04-12T15:35:44.019Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/fb/54b46b52c1fa2acd9afd81bd05810c61bb1b05c6084c9625b64bc6d41843/pycapnp-2.0.0.tar.gz", hash = "sha256:503ab9b7b16773590ee226f2460408972c6b1c2cb2d819037115b919bef682be", size = 574848, upload-time = "2024-04-12T15:35:44.019Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673, upload_time = "2024-04-12T15:33:32.211Z" }, - { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351, upload_time = "2024-04-12T15:33:35.156Z" }, - { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666, upload_time = "2024-04-12T15:33:37.798Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434, upload_time = "2024-04-12T15:33:40.362Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923, upload_time = "2024-04-12T15:33:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105, upload_time = "2024-04-12T15:33:44.294Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172, upload_time = "2024-04-12T15:33:46.954Z" }, - { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718, upload_time = "2024-04-12T15:33:49.587Z" }, - { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714, upload_time = "2024-04-12T15:33:51.518Z" }, - { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896, upload_time = "2024-04-12T15:33:53.716Z" }, - { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823, upload_time = "2024-04-12T15:33:55.573Z" }, - { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564, upload_time = "2024-04-12T15:33:59.112Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268, upload_time = "2024-04-12T15:34:00.933Z" }, - { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758, upload_time = "2024-04-12T15:34:02.607Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816, upload_time = "2024-04-12T15:34:04.428Z" }, - { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892, upload_time = "2024-04-12T15:34:06.933Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960, upload_time = "2024-04-12T15:34:08.771Z" }, - { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780, upload_time = "2024-04-12T15:34:10.863Z" }, - { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068, upload_time = "2024-04-12T15:34:13.543Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917, upload_time = "2024-04-12T15:34:15.636Z" }, - { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169, upload_time = "2024-04-12T15:34:17.758Z" }, - { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744, upload_time = "2024-04-12T15:34:20.468Z" }, - { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113, upload_time = "2024-04-12T15:34:23.173Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055, upload_time = "2024-04-12T15:34:25.799Z" }, - { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743, upload_time = "2024-04-12T15:34:28.134Z" }, - { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076, upload_time = "2024-04-12T15:34:30.305Z" }, - { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361, upload_time = "2024-04-12T15:34:32.25Z" }, - { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121, upload_time = "2024-04-12T15:34:34.208Z" }, + { url = "https://files.pythonhosted.org/packages/cb/82/cf311b1a9800b605759a38a0c337a55a639b685427364294e98a0f9b7306/pycapnp-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:829c7eb4e5f23dbcac25466110faf72a691035cf87c5d46e5053da15790e428d", size = 1673673, upload-time = "2024-04-12T15:33:32.211Z" }, + { url = "https://files.pythonhosted.org/packages/ae/55/4c03ca95c568776a1f637db9ffdcf302fb63f46e4d2a4f13edd8cb1a5f90/pycapnp-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab60fbe3e4eaf99ec97918a0a776216c6c149b6d49261383d91c2201adb475d", size = 1513351, upload-time = "2024-04-12T15:33:35.156Z" }, + { url = "https://files.pythonhosted.org/packages/55/98/e4b2dea076f8a2575abc45cd879a91bc9aa975c69ae2ac1cab61d83c5087/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c48a0582078bb74d7326d28571db0b8e6919563365537a5a13e8f5360c12bfc", size = 4910666, upload-time = "2024-04-12T15:33:37.798Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ee/3b5a182588f89074f4002fa6247e3f963bb85bc808acd1ac8deed91f1fa7/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb5ab54aff857e3711d2c0cc934194aaffacdeb3481daa56863daef07d27941", size = 5007434, upload-time = "2024-04-12T15:33:40.362Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e9/515a2ca7fdc84d57c654280d0b71dfd782fd1773d384c0ec0d56dc6fc35b/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9600778036e6fe9dbea68f0c37678c5f4d561d2f2306b3cb741de5e1670ef2ae", size = 5188923, upload-time = "2024-04-12T15:33:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/5db346e238985a526ba7589ed24f92195dad39e7dec9d85b17a567600b6f/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7278ba0262fab8c398e77d634ae7ba026866d44b52cbfc27262be8d396ecacd1", size = 5048105, upload-time = "2024-04-12T15:33:44.294Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/02b4a87c9ff9793f26d8f3d95312d902d260c094f216d84e19528a506606/pycapnp-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b2458d43c82302980a96518c96df257429204d2cc02bfff0c8cb6ebb371e01", size = 5063172, upload-time = "2024-04-12T15:33:46.954Z" }, + { url = "https://files.pythonhosted.org/packages/10/a1/35a7e14d765f99cfdcdfdcebc69bdf382f27016944470daa7a03c557f681/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd7755cc3fedc2ad8cc7864a0729471ddeff10c184963fe0f3689e295130f1b2", size = 5408718, upload-time = "2024-04-12T15:33:49.587Z" }, + { url = "https://files.pythonhosted.org/packages/5c/59/8bc8a993c38808c6fd90b10becba8de4a54543e8441bd87ce44ef3b7eee4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d47baf6b3db9981625ffc5ff188e089f2ebca8e7e1afb97aa5eb7bebb7bf3650", size = 5596714, upload-time = "2024-04-12T15:33:51.518Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7d/79c481ef77f29e81355e92bb250f0d2a37a76f5fe0ba9433bf6c6c88b6e4/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b375be92d93fdb6f7ac127ea9390bcec0fed4e485db137b084f9e7114dde7c83", size = 5709896, upload-time = "2024-04-12T15:33:53.716Z" }, + { url = "https://files.pythonhosted.org/packages/59/8d/f2eceeea1e8cae8b8a70a4752af5b772916f455e2ed388d0887e2b57d080/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:959bfdf1cddb3e5528e2293c4a375382be9a1bf044b073bc2e7eca1eb6b3a9a2", size = 5594823, upload-time = "2024-04-12T15:33:55.573Z" }, + { url = "https://files.pythonhosted.org/packages/2c/86/f8284637b61f83232e5618dd561a66080dd98ce2272d7e3ae89335d4fd97/pycapnp-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d873af167cf5cc7578ce5432eefcb442f866c8f7a6c57d188baf8c5e709fa39d", size = 5572564, upload-time = "2024-04-12T15:33:59.112Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b2/7f99d28a9935d1e37ec6955922c57b2be24fe0b74fe25929643686cc11e5/pycapnp-2.0.0-cp311-cp311-win32.whl", hash = "sha256:40ca8018e0b7686d549b920f087049b92a3e6f06976d9f5a8112603fc560cac4", size = 1040268, upload-time = "2024-04-12T15:34:00.933Z" }, + { url = "https://files.pythonhosted.org/packages/1d/37/89ab98961f18cffeae20d98cfc24afcfa85024bc014ecc48b0c4ac264fe0/pycapnp-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d15cd8e46d541a899c84809095d7d7b3951f43642d1859e7a39bd91910778479", size = 1141758, upload-time = "2024-04-12T15:34:02.607Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d5/0ee84de3ce34a86c373b6cfbea17d5486c2ca942d51efa99a0069723c1e3/pycapnp-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0c111ef96676df25b8afef98f369d45f838ad4434e2898e48199eb43ef704efe", size = 1645816, upload-time = "2024-04-12T15:34:04.428Z" }, + { url = "https://files.pythonhosted.org/packages/35/1e/580572083165ba791fac5ae2d8917facb94db6e3f0500421673f55165dac/pycapnp-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d18906eb1fd1b9f206d93a9591ceedce1d52e7766b66e68f271453f104e9dca", size = 1507892, upload-time = "2024-04-12T15:34:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ed/46b3cc5d32c525b6a3acb67eb43de2cec692a62775ec1ab66dafe2b7d6ad/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5d1ed365ab1beabb8838068907a7190cc0b6f16de3499d783627e670fcc0eb2", size = 4707960, upload-time = "2024-04-12T15:34:08.771Z" }, + { url = "https://files.pythonhosted.org/packages/8e/51/0a0a4d4e44138adb84959478ea4966196c5ad32022f768b9b64d1590cb3e/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:495b39a7aa2629931bbca27ad743ce591c6c41e8f81792276be424742d9cd1c1", size = 4791780, upload-time = "2024-04-12T15:34:10.863Z" }, + { url = "https://files.pythonhosted.org/packages/28/71/2b59c6ddb253b25b3d01ee6f7b32b0297ac205c7272beeb6d13399054430/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50e814fbde072dcc3d868b5b5cbb9b7a66a70bff9ad03942f3be9baf3ca1cfc6", size = 4961068, upload-time = "2024-04-12T15:34:13.543Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b8/b64fdefa59d6d2802b5ee0a9439396c23a3e5954da6909be81f2722a234c/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:920fdda62d5fdef7a48339104dff0ceb9dcc21b138491f854457ba3a3d4d63ec", size = 4872917, upload-time = "2024-04-12T15:34:15.636Z" }, + { url = "https://files.pythonhosted.org/packages/c8/55/867595f575eb6cb3662e9a0b50a24b4be42df86f2938003e586f6c81606f/pycapnp-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f9142eb4714c152b09dda0b055ea9dd43fd8fd894132e7eb4fa235fb4915edd", size = 4912169, upload-time = "2024-04-12T15:34:17.758Z" }, + { url = "https://files.pythonhosted.org/packages/e4/11/0d36b45e5005ecdf8510081d16c6fb7b22b49651f64af36d138df97980cc/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c98f1d0c4d32109d03e42828ce3c65236afc895033633cbed3ca092993702e7b", size = 5201744, upload-time = "2024-04-12T15:34:20.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/29/ad1357998656b7141939e55bb3aea727c7a5478026feed7f8ee8cf52c935/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4d3250c1875a309d67551843cd8bf3c5e7fccf159b7f5c118a92aee36c0e871c", size = 5351113, upload-time = "2024-04-12T15:34:23.173Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b1/f4c442907948a29b6427dd7436f31d3732bb0d77f5c1dbcad749ba56dac0/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:174e6babe01f5507111c0ed226cd0b5e9325a9d2850751cfe4a57c1670f13881", size = 5472055, upload-time = "2024-04-12T15:34:25.799Z" }, + { url = "https://files.pythonhosted.org/packages/c1/06/a6eceb8b8015f518c0ccae1de5d1a6e18ed73b62b4b111aff54ce5f4f566/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:ed38ece414341285695526792e020f391f29f5064b2126d0367c8bdeef28e3e9", size = 5395743, upload-time = "2024-04-12T15:34:28.134Z" }, + { url = "https://files.pythonhosted.org/packages/e7/b0/63f2b0327853ae08158de61b4dfc7fa43ae5a5c00f1d28f769e7c30cdf55/pycapnp-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a20b7dc55ef83a1fa446bf12680bce25caeb8f81788b623b072c3ec820db50d", size = 5405076, upload-time = "2024-04-12T15:34:30.305Z" }, + { url = "https://files.pythonhosted.org/packages/7d/24/e025dd95f1abf34e373fbab8841ac8e5fa62afe3af4a4b0c61bd01354400/pycapnp-2.0.0-cp312-cp312-win32.whl", hash = "sha256:145eea66233fb5ac9152cd1c06b999ddb691815126f87f5cc37b9cda5d569f8a", size = 1030361, upload-time = "2024-04-12T15:34:32.25Z" }, + { url = "https://files.pythonhosted.org/packages/3f/70/a71108ee9d4db9a027b665a2c383202407207174f1956195d5be45aca705/pycapnp-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:b8b03000769b29b36a8810f458b931f0f706f42027ee6676821eff28092d7734", size = 1135121, upload-time = "2024-04-12T15:34:34.208Z" }, ] [[package]] name = "pycparser" version = "2.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload_time = "2024-03-30T13:22:22.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload_time = "2024-03-30T13:22:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, ] [[package]] name = "pycryptodome" version = "3.22.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/e6/099310419df5ada522ff34ffc2f1a48a11b37fc6a76f51a6854c182dbd3e/pycryptodome-3.22.0.tar.gz", hash = "sha256:fd7ab568b3ad7b77c908d7c3f7e167ec5a8f035c64ff74f10d47a4edd043d723", size = 4917300, upload_time = "2025-03-15T23:03:36.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/e6/099310419df5ada522ff34ffc2f1a48a11b37fc6a76f51a6854c182dbd3e/pycryptodome-3.22.0.tar.gz", hash = "sha256:fd7ab568b3ad7b77c908d7c3f7e167ec5a8f035c64ff74f10d47a4edd043d723", size = 4917300, upload-time = "2025-03-15T23:03:36.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/65/a05831c3e4bcd1bf6c2a034e399f74b3d6f30bb4e37e36b9c310c09dc8c0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:009e1c80eea42401a5bd5983c4bab8d516aef22e014a4705622e24e6d9d703c6", size = 2490637, upload_time = "2025-03-15T23:02:43.111Z" }, - { url = "https://files.pythonhosted.org/packages/5c/76/ff3c2e7a60d17c080c4c6120ebaf60f38717cd387e77f84da4dcf7f64ff0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3b76fa80daeff9519d7e9f6d9e40708f2fce36b9295a847f00624a08293f4f00", size = 1635372, upload_time = "2025-03-15T23:02:45.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/7f/cc5d6da0dbc36acd978d80a72b228e33aadaec9c4f91c93221166d8bdc05/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a31fa5914b255ab62aac9265654292ce0404f6b66540a065f538466474baedbc", size = 2177456, upload_time = "2025-03-15T23:02:47.688Z" }, - { url = "https://files.pythonhosted.org/packages/92/65/35f5063e68790602d892ad36e35ac723147232a9084d1999630045c34593/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0092fd476701eeeb04df5cc509d8b739fa381583cda6a46ff0a60639b7cd70d", size = 2263744, upload_time = "2025-03-15T23:02:49.548Z" }, - { url = "https://files.pythonhosted.org/packages/cc/67/46acdd35b1081c3dbc72dc466b1b95b80d2f64cad3520f994a9b6c5c7d00/pycryptodome-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d5b0ddc7cf69231736d778bd3ae2b3efb681ae33b64b0c92fb4626bb48bb89", size = 2303356, upload_time = "2025-03-15T23:02:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f9/a4f8a83384626098e3f55664519bec113002b9ef751887086ae63a53135a/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f6cf6aa36fcf463e622d2165a5ad9963b2762bebae2f632d719dfb8544903cf5", size = 2176714, upload_time = "2025-03-15T23:02:53.85Z" }, - { url = "https://files.pythonhosted.org/packages/88/65/e5f8c3a885f70a6e05c84844cd5542120576f4369158946e8cfc623a464d/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec7b40a7ea5af7c40f8837adf20a137d5e11a6eb202cde7e588a48fb2d871a8", size = 2337329, upload_time = "2025-03-15T23:02:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255, upload_time = "2025-03-15T23:02:58.055Z" }, - { url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403, upload_time = "2025-03-15T23:03:00.616Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568, upload_time = "2025-03-15T23:03:03.189Z" }, + { url = "https://files.pythonhosted.org/packages/1f/65/a05831c3e4bcd1bf6c2a034e399f74b3d6f30bb4e37e36b9c310c09dc8c0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:009e1c80eea42401a5bd5983c4bab8d516aef22e014a4705622e24e6d9d703c6", size = 2490637, upload-time = "2025-03-15T23:02:43.111Z" }, + { url = "https://files.pythonhosted.org/packages/5c/76/ff3c2e7a60d17c080c4c6120ebaf60f38717cd387e77f84da4dcf7f64ff0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3b76fa80daeff9519d7e9f6d9e40708f2fce36b9295a847f00624a08293f4f00", size = 1635372, upload-time = "2025-03-15T23:02:45.564Z" }, + { url = "https://files.pythonhosted.org/packages/cc/7f/cc5d6da0dbc36acd978d80a72b228e33aadaec9c4f91c93221166d8bdc05/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a31fa5914b255ab62aac9265654292ce0404f6b66540a065f538466474baedbc", size = 2177456, upload-time = "2025-03-15T23:02:47.688Z" }, + { url = "https://files.pythonhosted.org/packages/92/65/35f5063e68790602d892ad36e35ac723147232a9084d1999630045c34593/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0092fd476701eeeb04df5cc509d8b739fa381583cda6a46ff0a60639b7cd70d", size = 2263744, upload-time = "2025-03-15T23:02:49.548Z" }, + { url = "https://files.pythonhosted.org/packages/cc/67/46acdd35b1081c3dbc72dc466b1b95b80d2f64cad3520f994a9b6c5c7d00/pycryptodome-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d5b0ddc7cf69231736d778bd3ae2b3efb681ae33b64b0c92fb4626bb48bb89", size = 2303356, upload-time = "2025-03-15T23:02:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/a4f8a83384626098e3f55664519bec113002b9ef751887086ae63a53135a/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f6cf6aa36fcf463e622d2165a5ad9963b2762bebae2f632d719dfb8544903cf5", size = 2176714, upload-time = "2025-03-15T23:02:53.85Z" }, + { url = "https://files.pythonhosted.org/packages/88/65/e5f8c3a885f70a6e05c84844cd5542120576f4369158946e8cfc623a464d/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec7b40a7ea5af7c40f8837adf20a137d5e11a6eb202cde7e588a48fb2d871a8", size = 2337329, upload-time = "2025-03-15T23:02:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255, upload-time = "2025-03-15T23:02:58.055Z" }, + { url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403, upload-time = "2025-03-15T23:03:00.616Z" }, + { url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568, upload-time = "2025-03-15T23:03:03.189Z" }, ] [[package]] @@ -1754,31 +1754,31 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload_time = "2025-03-17T18:53:15.955Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload_time = "2025-03-17T18:53:14.532Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" }, ] [[package]] name = "pygame" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/cc/08bba60f00541f62aaa252ce0cfbd60aebd04616c0b9574f755b583e45ae/pygame-2.6.1.tar.gz", hash = "sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f", size = 14808125, upload_time = "2024-09-29T13:41:34.698Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/cc/08bba60f00541f62aaa252ce0cfbd60aebd04616c0b9574f755b583e45ae/pygame-2.6.1.tar.gz", hash = "sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f", size = 14808125, upload-time = "2024-09-29T13:41:34.698Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ca/8f367cb9fe734c4f6f6400e045593beea2635cd736158f9fabf58ee14e3c/pygame-2.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:20349195326a5e82a16e351ed93465a7845a7e2a9af55b7bc1b2110ea3e344e1", size = 13113753, upload_time = "2024-09-29T14:26:13.751Z" }, - { url = "https://files.pythonhosted.org/packages/83/47/6edf2f890139616b3219be9cfcc8f0cb8f42eb15efd59597927e390538cb/pygame-2.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3935459109da4bb0b3901da9904f0a3e52028a3332a355d298b1673a334cf21", size = 12378146, upload_time = "2024-09-29T14:26:22.456Z" }, - { url = "https://files.pythonhosted.org/packages/00/9e/0d8aa8cf93db2d2ee38ebaf1c7b61d0df36ded27eb726221719c150c673d/pygame-2.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31dbdb5d0217f32764797d21c2752e258e5fb7e895326538d82b5f75a0cd856", size = 13611760, upload_time = "2024-09-29T11:10:47.317Z" }, - { url = "https://files.pythonhosted.org/packages/d7/9e/d06adaa5cc65876bcd7a24f59f67e07f7e4194e6298130024ed3fb22c456/pygame-2.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:173badf82fa198e6888017bea40f511cb28e69ecdd5a72b214e81e4dcd66c3b1", size = 14298054, upload_time = "2024-09-29T11:39:53.891Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a1/9ae2852ebd3a7cc7d9ae7ff7919ab983e4a5c1b7a14e840732f23b2b48f6/pygame-2.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce8cc108b92de9b149b344ad2e25eedbe773af0dc41dfb24d1f07f679b558c60", size = 13977107, upload_time = "2024-09-29T11:39:56.831Z" }, - { url = "https://files.pythonhosted.org/packages/31/df/6788fd2e9a864d0496a77670e44a7c012184b7a5382866ab0e60c55c0f28/pygame-2.6.1-cp311-cp311-win32.whl", hash = "sha256:811e7b925146d8149d79193652cbb83e0eca0aae66476b1cb310f0f4226b8b5c", size = 10250863, upload_time = "2024-09-29T11:44:48.199Z" }, - { url = "https://files.pythonhosted.org/packages/d2/55/ca3eb851aeef4f6f2e98a360c201f0d00bd1ba2eb98e2c7850d80aabc526/pygame-2.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:91476902426facd4bb0dad4dc3b2573bc82c95c71b135e0daaea072ed528d299", size = 10622016, upload_time = "2024-09-29T12:17:01.545Z" }, - { url = "https://files.pythonhosted.org/packages/92/16/2c602c332f45ff9526d61f6bd764db5096ff9035433e2172e2d2cadae8db/pygame-2.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4ee7f2771f588c966fa2fa8b829be26698c9b4836f82ede5e4edc1a68594942e", size = 13118279, upload_time = "2024-09-29T14:26:30.427Z" }, - { url = "https://files.pythonhosted.org/packages/cd/53/77ccbc384b251c6e34bfd2e734c638233922449a7844e3c7a11ef91cee39/pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8040ea2ab18c6b255af706ec01355c8a6b08dc48d77fd4ee783f8fc46a843bf", size = 12384524, upload_time = "2024-09-29T14:26:49.996Z" }, - { url = "https://files.pythonhosted.org/packages/06/be/3ed337583f010696c3b3435e89a74fb29d0c74d0931e8f33c0a4246307a9/pygame-2.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47a6938de93fa610accd4969e638c2aebcb29b2fca518a84c3a39d91ab47116", size = 13587123, upload_time = "2024-09-29T11:10:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/b015586a450db59313535662991b34d24c1f0c0dc149cc5f496573900f4e/pygame-2.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33006f784e1c7d7e466fcb61d5489da59cc5f7eb098712f792a225df1d4e229d", size = 14275532, upload_time = "2024-09-29T11:39:59.356Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f2/d31e6ad42d657af07be2ffd779190353f759a07b51232b9e1d724f2cda46/pygame-2.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1206125f14cae22c44565c9d333607f1d9f59487b1f1432945dfc809aeaa3e88", size = 13952653, upload_time = "2024-09-29T11:40:01.781Z" }, - { url = "https://files.pythonhosted.org/packages/f3/42/8ea2a6979e6fa971702fece1747e862e2256d4a8558fe0da6364dd946c53/pygame-2.6.1-cp312-cp312-win32.whl", hash = "sha256:84fc4054e25262140d09d39e094f6880d730199710829902f0d8ceae0213379e", size = 10252421, upload_time = "2024-09-29T11:14:26.877Z" }, - { url = "https://files.pythonhosted.org/packages/5f/90/7d766d54bb95939725e9a9361f9c06b0cfbe3fe100aa35400f0a461a278a/pygame-2.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65", size = 10624591, upload_time = "2024-09-29T11:52:54.489Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ca/8f367cb9fe734c4f6f6400e045593beea2635cd736158f9fabf58ee14e3c/pygame-2.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:20349195326a5e82a16e351ed93465a7845a7e2a9af55b7bc1b2110ea3e344e1", size = 13113753, upload-time = "2024-09-29T14:26:13.751Z" }, + { url = "https://files.pythonhosted.org/packages/83/47/6edf2f890139616b3219be9cfcc8f0cb8f42eb15efd59597927e390538cb/pygame-2.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3935459109da4bb0b3901da9904f0a3e52028a3332a355d298b1673a334cf21", size = 12378146, upload-time = "2024-09-29T14:26:22.456Z" }, + { url = "https://files.pythonhosted.org/packages/00/9e/0d8aa8cf93db2d2ee38ebaf1c7b61d0df36ded27eb726221719c150c673d/pygame-2.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31dbdb5d0217f32764797d21c2752e258e5fb7e895326538d82b5f75a0cd856", size = 13611760, upload-time = "2024-09-29T11:10:47.317Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9e/d06adaa5cc65876bcd7a24f59f67e07f7e4194e6298130024ed3fb22c456/pygame-2.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:173badf82fa198e6888017bea40f511cb28e69ecdd5a72b214e81e4dcd66c3b1", size = 14298054, upload-time = "2024-09-29T11:39:53.891Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a1/9ae2852ebd3a7cc7d9ae7ff7919ab983e4a5c1b7a14e840732f23b2b48f6/pygame-2.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce8cc108b92de9b149b344ad2e25eedbe773af0dc41dfb24d1f07f679b558c60", size = 13977107, upload-time = "2024-09-29T11:39:56.831Z" }, + { url = "https://files.pythonhosted.org/packages/31/df/6788fd2e9a864d0496a77670e44a7c012184b7a5382866ab0e60c55c0f28/pygame-2.6.1-cp311-cp311-win32.whl", hash = "sha256:811e7b925146d8149d79193652cbb83e0eca0aae66476b1cb310f0f4226b8b5c", size = 10250863, upload-time = "2024-09-29T11:44:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/d2/55/ca3eb851aeef4f6f2e98a360c201f0d00bd1ba2eb98e2c7850d80aabc526/pygame-2.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:91476902426facd4bb0dad4dc3b2573bc82c95c71b135e0daaea072ed528d299", size = 10622016, upload-time = "2024-09-29T12:17:01.545Z" }, + { url = "https://files.pythonhosted.org/packages/92/16/2c602c332f45ff9526d61f6bd764db5096ff9035433e2172e2d2cadae8db/pygame-2.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4ee7f2771f588c966fa2fa8b829be26698c9b4836f82ede5e4edc1a68594942e", size = 13118279, upload-time = "2024-09-29T14:26:30.427Z" }, + { url = "https://files.pythonhosted.org/packages/cd/53/77ccbc384b251c6e34bfd2e734c638233922449a7844e3c7a11ef91cee39/pygame-2.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8040ea2ab18c6b255af706ec01355c8a6b08dc48d77fd4ee783f8fc46a843bf", size = 12384524, upload-time = "2024-09-29T14:26:49.996Z" }, + { url = "https://files.pythonhosted.org/packages/06/be/3ed337583f010696c3b3435e89a74fb29d0c74d0931e8f33c0a4246307a9/pygame-2.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47a6938de93fa610accd4969e638c2aebcb29b2fca518a84c3a39d91ab47116", size = 13587123, upload-time = "2024-09-29T11:10:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/b015586a450db59313535662991b34d24c1f0c0dc149cc5f496573900f4e/pygame-2.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33006f784e1c7d7e466fcb61d5489da59cc5f7eb098712f792a225df1d4e229d", size = 14275532, upload-time = "2024-09-29T11:39:59.356Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f2/d31e6ad42d657af07be2ffd779190353f759a07b51232b9e1d724f2cda46/pygame-2.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1206125f14cae22c44565c9d333607f1d9f59487b1f1432945dfc809aeaa3e88", size = 13952653, upload-time = "2024-09-29T11:40:01.781Z" }, + { url = "https://files.pythonhosted.org/packages/f3/42/8ea2a6979e6fa971702fece1747e862e2256d4a8558fe0da6364dd946c53/pygame-2.6.1-cp312-cp312-win32.whl", hash = "sha256:84fc4054e25262140d09d39e094f6880d730199710829902f0d8ceae0213379e", size = 10252421, upload-time = "2024-09-29T11:14:26.877Z" }, + { url = "https://files.pythonhosted.org/packages/5f/90/7d766d54bb95939725e9a9361f9c06b0cfbe3fe100aa35400f0a461a278a/pygame-2.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65", size = 10624591, upload-time = "2024-09-29T11:52:54.489Z" }, ] [[package]] @@ -1788,24 +1788,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyrect" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699, upload_time = "2020-10-04T02:12:50.806Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz", hash = "sha256:17894355e7d2b305cd832d717708384017c1698a90ce24f6f7fbf0242dd0a688", size = 9699, upload-time = "2020-10-04T02:12:50.806Z" } [[package]] name = "pygments" version = "2.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload_time = "2025-01-06T17:26:30.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload_time = "2025-01-06T17:26:25.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, ] [[package]] name = "pyjwt" version = "2.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload_time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload_time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, ] [package.optional-dependencies] @@ -1820,18 +1820,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/c8/a59e61f5dd655f5f21033bd643dd31fe980a537ed6f373cdfb49d3a3bd32/pylibsrtp-0.12.0.tar.gz", hash = "sha256:f5c3c0fb6954e7bb74dc7e6398352740ca67327e6759a199fe852dbc7b84b8ac", size = 10878, upload_time = "2025-04-06T12:35:51.804Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/c8/a59e61f5dd655f5f21033bd643dd31fe980a537ed6f373cdfb49d3a3bd32/pylibsrtp-0.12.0.tar.gz", hash = "sha256:f5c3c0fb6954e7bb74dc7e6398352740ca67327e6759a199fe852dbc7b84b8ac", size = 10878, upload-time = "2025-04-06T12:35:51.804Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f0/b818395c4cae2d5cc5a0c78fc47d694eae78e6a0d678baeb52a381a26327/pylibsrtp-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5adde3cf9a5feef561d0eb7ed99dedb30b9bf1ce9a0c1770b2bf19fd0b98bc9a", size = 1727918, upload_time = "2025-04-06T12:35:36.456Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/ee553abe4431b7bd9bab18f078c0ad2298b94ea55e664da6ecb8700b1052/pylibsrtp-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d2c81d152606721331ece87c80ed17159ba6da55c7c61a6b750cff67ab7f63a5", size = 2057900, upload_time = "2025-04-06T12:35:38.253Z" }, - { url = "https://files.pythonhosted.org/packages/7f/a2/2dd0188be58d3cba48c5eb4b3c787e5743c111cd0c9289de4b6f2798382a/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:242fa3d44219846bf1734d5df595563a2c8fbb0fb00ccc79ab0f569fc0af2c1b", size = 2567047, upload_time = "2025-04-06T12:35:39.797Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3a/4bdab9fc1d78f2efa02c8a8f3e9c187bfa278e89481b5123f07c8dd69310/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74aaf8fac1b119a3c762f54751c3d20e77227b84c26d85aae57c2c43129b49c", size = 2168775, upload_time = "2025-04-06T12:35:41.422Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fc/0b1e1bfed420d79427d50aff84c370dcd78d81af9500c1e86fbcc5bf95e1/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e3e223102989b71f07e1deeb804170ed53fb4e1b283762eb031bd45bb425d4", size = 2225033, upload_time = "2025-04-06T12:35:43.03Z" }, - { url = "https://files.pythonhosted.org/packages/39/7b/e1021d27900315c2c077ec7d45f50274cedbdde067ff679d44df06f01a8a/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:36d07de64dbc82dbbb99fd77f36c8e23d6730bdbcccf09701945690a9a9a422a", size = 2606093, upload_time = "2025-04-06T12:35:44.587Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c2/0fae6687a06fcde210a778148ec808af49e431c36fe9908503a695c35479/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ef03b4578577690f716fd023daed8914eee6de9a764fa128eda19a0e645cc032", size = 2193213, upload_time = "2025-04-06T12:35:46.167Z" }, - { url = "https://files.pythonhosted.org/packages/67/c2/2ed7a4a5c38b999fd34298f76b93d29f5ba8c06f85cfad3efd9468343715/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a8421e9fe4d20ce48d439430e55149f12b1bca1b0436741972c362c49948c0a", size = 2256774, upload_time = "2025-04-06T12:35:47.704Z" }, - { url = "https://files.pythonhosted.org/packages/48/d7/f13fedce3b21d24f6f154d1dee7287464a34728dcb3b0c50f687dbad5765/pylibsrtp-0.12.0-cp39-abi3-win32.whl", hash = "sha256:cbc9bfbfb2597e993a1aa16b832ba16a9dd4647f70815421bb78484f8b50b924", size = 1156186, upload_time = "2025-04-06T12:35:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/9b/26/3a20b638a3a3995368f856eeb10701dd6c0e9ace9fb6665eeb1b95ccce19/pylibsrtp-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:061ef1dbb5f08079ac6d7515b7e67ca48a3163e16e5b820beea6b01cb31d7e54", size = 1485072, upload_time = "2025-04-06T12:35:50.312Z" }, + { url = "https://files.pythonhosted.org/packages/65/f0/b818395c4cae2d5cc5a0c78fc47d694eae78e6a0d678baeb52a381a26327/pylibsrtp-0.12.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:5adde3cf9a5feef561d0eb7ed99dedb30b9bf1ce9a0c1770b2bf19fd0b98bc9a", size = 1727918, upload-time = "2025-04-06T12:35:36.456Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/ee553abe4431b7bd9bab18f078c0ad2298b94ea55e664da6ecb8700b1052/pylibsrtp-0.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d2c81d152606721331ece87c80ed17159ba6da55c7c61a6b750cff67ab7f63a5", size = 2057900, upload-time = "2025-04-06T12:35:38.253Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a2/2dd0188be58d3cba48c5eb4b3c787e5743c111cd0c9289de4b6f2798382a/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:242fa3d44219846bf1734d5df595563a2c8fbb0fb00ccc79ab0f569fc0af2c1b", size = 2567047, upload-time = "2025-04-06T12:35:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3a/4bdab9fc1d78f2efa02c8a8f3e9c187bfa278e89481b5123f07c8dd69310/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74aaf8fac1b119a3c762f54751c3d20e77227b84c26d85aae57c2c43129b49c", size = 2168775, upload-time = "2025-04-06T12:35:41.422Z" }, + { url = "https://files.pythonhosted.org/packages/d0/fc/0b1e1bfed420d79427d50aff84c370dcd78d81af9500c1e86fbcc5bf95e1/pylibsrtp-0.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e3e223102989b71f07e1deeb804170ed53fb4e1b283762eb031bd45bb425d4", size = 2225033, upload-time = "2025-04-06T12:35:43.03Z" }, + { url = "https://files.pythonhosted.org/packages/39/7b/e1021d27900315c2c077ec7d45f50274cedbdde067ff679d44df06f01a8a/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:36d07de64dbc82dbbb99fd77f36c8e23d6730bdbcccf09701945690a9a9a422a", size = 2606093, upload-time = "2025-04-06T12:35:44.587Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c2/0fae6687a06fcde210a778148ec808af49e431c36fe9908503a695c35479/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:ef03b4578577690f716fd023daed8914eee6de9a764fa128eda19a0e645cc032", size = 2193213, upload-time = "2025-04-06T12:35:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/67/c2/2ed7a4a5c38b999fd34298f76b93d29f5ba8c06f85cfad3efd9468343715/pylibsrtp-0.12.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0a8421e9fe4d20ce48d439430e55149f12b1bca1b0436741972c362c49948c0a", size = 2256774, upload-time = "2025-04-06T12:35:47.704Z" }, + { url = "https://files.pythonhosted.org/packages/48/d7/f13fedce3b21d24f6f154d1dee7287464a34728dcb3b0c50f687dbad5765/pylibsrtp-0.12.0-cp39-abi3-win32.whl", hash = "sha256:cbc9bfbfb2597e993a1aa16b832ba16a9dd4647f70815421bb78484f8b50b924", size = 1156186, upload-time = "2025-04-06T12:35:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/9b/26/3a20b638a3a3995368f856eeb10701dd6c0e9ace9fb6665eeb1b95ccce19/pylibsrtp-0.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:061ef1dbb5f08079ac6d7515b7e67ca48a3163e16e5b820beea6b01cb31d7e54", size = 1485072, upload-time = "2025-04-06T12:35:50.312Z" }, ] [[package]] @@ -1846,14 +1846,14 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/13/076a20da28b82be281f7e43e16d9da0f545090f5d14b2125699232b9feba/PyMonCtl-0.92-py3-none-any.whl", hash = "sha256:2495d8dab78f9a7dbce37e74543e60b8bd404a35c3108935697dda7768611b5a", size = 45945, upload_time = "2024-04-22T10:07:09.566Z" }, + { url = "https://files.pythonhosted.org/packages/2d/13/076a20da28b82be281f7e43e16d9da0f545090f5d14b2125699232b9feba/PyMonCtl-0.92-py3-none-any.whl", hash = "sha256:2495d8dab78f9a7dbce37e74543e60b8bd404a35c3108935697dda7768611b5a", size = 45945, upload-time = "2024-04-22T10:07:09.566Z" }, ] [[package]] name = "pymsgbox" version = "1.0.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829, upload_time = "2020-10-11T01:51:43.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz", hash = "sha256:2194227de8bff7a3d6da541848705a155dcbb2a06ee120d9f280a1d7f51263ff", size = 18829, upload-time = "2020-10-11T01:51:43.227Z" } [[package]] name = "pyobjc" @@ -2017,19 +2017,19 @@ dependencies = [ { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0'" }, { name = "pyobjc-framework-webkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044, upload_time = "2025-01-14T19:02:12.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044, upload-time = "2025-01-14T19:02:12.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/55/d0971bccf8a5a347eaccf8caa4718766a68281baab83d2b5e211b2767504/pyobjc-11.0-py3-none-any.whl", hash = "sha256:3ed5e4e993192fd7fadd42a4148d266a3587af7453ea3b240bab724d02e34e64", size = 4169, upload_time = "2025-01-14T18:46:44.385Z" }, + { url = "https://files.pythonhosted.org/packages/18/55/d0971bccf8a5a347eaccf8caa4718766a68281baab83d2b5e211b2767504/pyobjc-11.0-py3-none-any.whl", hash = "sha256:3ed5e4e993192fd7fadd42a4148d266a3587af7453ea3b240bab724d02e34e64", size = 4169, upload-time = "2025-01-14T18:46:44.385Z" }, ] [[package]] name = "pyobjc-core" version = "11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/94/a111239b98260869780a5767e5d74bfd3a8c13a40457f479c28dcd91f89d/pyobjc_core-11.0.tar.gz", hash = "sha256:63bced211cb8a8fb5c8ff46473603da30e51112861bd02c438fbbbc8578d9a70", size = 994931, upload_time = "2025-01-14T19:02:13.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/94/a111239b98260869780a5767e5d74bfd3a8c13a40457f479c28dcd91f89d/pyobjc_core-11.0.tar.gz", hash = "sha256:63bced211cb8a8fb5c8ff46473603da30e51112861bd02c438fbbbc8578d9a70", size = 994931, upload-time = "2025-01-14T19:02:13.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/05/fa97309c3b1bc1ec90d701db89902e0bd5e1024023aa2c5387b889458b1b/pyobjc_core-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50675c0bb8696fe960a28466f9baf6943df2928a1fd85625d678fa2f428bd0bd", size = 727295, upload_time = "2025-01-14T18:46:50.208Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/bf3ff9a9347721a398c3dfb83e29b43fb166b7ef590f3f7b7ddcd283df39/pyobjc_core-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a03061d4955c62ddd7754224a80cdadfdf17b6b5f60df1d9169a3b1b02923f0b", size = 739750, upload_time = "2025-01-14T18:46:53.039Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/fa97309c3b1bc1ec90d701db89902e0bd5e1024023aa2c5387b889458b1b/pyobjc_core-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50675c0bb8696fe960a28466f9baf6943df2928a1fd85625d678fa2f428bd0bd", size = 727295, upload-time = "2025-01-14T18:46:50.208Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/bf3ff9a9347721a398c3dfb83e29b43fb166b7ef590f3f7b7ddcd283df39/pyobjc_core-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a03061d4955c62ddd7754224a80cdadfdf17b6b5f60df1d9169a3b1b02923f0b", size = 739750, upload-time = "2025-01-14T18:46:53.039Z" }, ] [[package]] @@ -2041,10 +2041,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597, upload_time = "2025-01-14T19:02:17.596Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597, upload-time = "2025-01-14T19:02:17.596Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/2e/babcd02cd833c0aba34e10c34a2184021b8a3c7cb45d1ae806156c2b519d/pyobjc_framework_Accessibility-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c3a751d17b288bb56a98a10b52b253b3002c885fe686b604788acac1e9739437", size = 10948, upload_time = "2025-01-14T18:47:34.37Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ea/da3f982eeaffb80efb480892106caa19a2c9c8b8954570837ddbcd983520/pyobjc_framework_Accessibility-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:34536f3d60aeda618b384b1207a8c6f9978de278ce229c3469ef14fd27a3befa", size = 10962, upload_time = "2025-01-14T18:47:35.313Z" }, + { url = "https://files.pythonhosted.org/packages/52/2e/babcd02cd833c0aba34e10c34a2184021b8a3c7cb45d1ae806156c2b519d/pyobjc_framework_Accessibility-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c3a751d17b288bb56a98a10b52b253b3002c885fe686b604788acac1e9739437", size = 10948, upload-time = "2025-01-14T18:47:34.37Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ea/da3f982eeaffb80efb480892106caa19a2c9c8b8954570837ddbcd983520/pyobjc_framework_Accessibility-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:34536f3d60aeda618b384b1207a8c6f9978de278ce229c3469ef14fd27a3befa", size = 10962, upload-time = "2025-01-14T18:47:35.313Z" }, ] [[package]] @@ -2055,10 +2055,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340, upload_time = "2025-01-14T19:02:18.625Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340, upload-time = "2025-01-14T19:02:18.625Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/45/5dfc72c82087d458ce7ddb17a338a38ae1848e72620537f31ed97192c65e/pyobjc_framework_Accounts-11.0-py2.py3-none-any.whl", hash = "sha256:3e4b494e1158e3250e4b4a09e9ff33b38f82d31aefe50dd47152c4a20ecdeec4", size = 5035, upload_time = "2025-01-14T18:47:40.92Z" }, - { url = "https://files.pythonhosted.org/packages/96/96/39b0cc9ced1180a93c75924a06598f24d0a7554b3e8ddfcb0828c0957476/pyobjc_framework_Accounts-11.0-py3-none-any.whl", hash = "sha256:ad0e378bd07ca7c88b45cda63b85424bc344e81ea44c0ae7327872d91cad311a", size = 5104, upload_time = "2025-01-14T18:47:41.967Z" }, + { url = "https://files.pythonhosted.org/packages/93/45/5dfc72c82087d458ce7ddb17a338a38ae1848e72620537f31ed97192c65e/pyobjc_framework_Accounts-11.0-py2.py3-none-any.whl", hash = "sha256:3e4b494e1158e3250e4b4a09e9ff33b38f82d31aefe50dd47152c4a20ecdeec4", size = 5035, upload-time = "2025-01-14T18:47:40.92Z" }, + { url = "https://files.pythonhosted.org/packages/96/96/39b0cc9ced1180a93c75924a06598f24d0a7554b3e8ddfcb0828c0957476/pyobjc_framework_Accounts-11.0-py3-none-any.whl", hash = "sha256:ad0e378bd07ca7c88b45cda63b85424bc344e81ea44c0ae7327872d91cad311a", size = 5104, upload-time = "2025-01-14T18:47:41.967Z" }, ] [[package]] @@ -2069,10 +2069,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116, upload_time = "2025-01-14T19:02:19.527Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116, upload-time = "2025-01-14T19:02:19.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/7a/8b874a52ff57dad999330ac1f899e6df8e35cec2cad8a90d8002d3c5f196/pyobjc_framework_AddressBook-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:af7de23aac7571a3b9dad5b2881d8f186653aa72903db8d7dbfd2c7b993156b9", size = 13010, upload_time = "2025-01-14T18:47:47.925Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b4/93de1195c22cbaf4996aeb6d55e79fc7d76311cacfe8fd716c70fb20e391/pyobjc_framework_AddressBook-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b634ef80920ab9208f2937527e4a498e7afa6e2ceb639ebb483387ab5b9accc", size = 13039, upload_time = "2025-01-14T18:47:49.509Z" }, + { url = "https://files.pythonhosted.org/packages/2d/7a/8b874a52ff57dad999330ac1f899e6df8e35cec2cad8a90d8002d3c5f196/pyobjc_framework_AddressBook-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:af7de23aac7571a3b9dad5b2881d8f186653aa72903db8d7dbfd2c7b993156b9", size = 13010, upload-time = "2025-01-14T18:47:47.925Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b4/93de1195c22cbaf4996aeb6d55e79fc7d76311cacfe8fd716c70fb20e391/pyobjc_framework_AddressBook-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b634ef80920ab9208f2937527e4a498e7afa6e2ceb639ebb483387ab5b9accc", size = 13039, upload-time = "2025-01-14T18:47:49.509Z" }, ] [[package]] @@ -2083,10 +2083,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732, upload_time = "2025-01-14T19:02:20.559Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732, upload-time = "2025-01-14T19:02:20.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/10/601c9f5a07450ce75e166042d9ac5efe6286ac2d15212885a920260af9e3/pyobjc_framework_AdServices-11.0-py2.py3-none-any.whl", hash = "sha256:7cd1458f60175cd46bd88061c20e82f04b2576fc00bc5d54d67c18dcb870e27f", size = 3420, upload_time = "2025-01-14T18:47:42.812Z" }, - { url = "https://files.pythonhosted.org/packages/89/40/98a9116790e163d6c9ac0d19ce66307b03f9ac5ee64631db69899457b154/pyobjc_framework_AdServices-11.0-py3-none-any.whl", hash = "sha256:6426d4e4a43f5ee5ce7bab44d85647dbded3e17c0c62d8923cebaf927c4162ca", size = 3486, upload_time = "2025-01-14T18:47:43.845Z" }, + { url = "https://files.pythonhosted.org/packages/1d/10/601c9f5a07450ce75e166042d9ac5efe6286ac2d15212885a920260af9e3/pyobjc_framework_AdServices-11.0-py2.py3-none-any.whl", hash = "sha256:7cd1458f60175cd46bd88061c20e82f04b2576fc00bc5d54d67c18dcb870e27f", size = 3420, upload-time = "2025-01-14T18:47:42.812Z" }, + { url = "https://files.pythonhosted.org/packages/89/40/98a9116790e163d6c9ac0d19ce66307b03f9ac5ee64631db69899457b154/pyobjc_framework_AdServices-11.0-py3-none-any.whl", hash = "sha256:6426d4e4a43f5ee5ce7bab44d85647dbded3e17c0c62d8923cebaf927c4162ca", size = 3486, upload-time = "2025-01-14T18:47:43.845Z" }, ] [[package]] @@ -2097,10 +2097,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819, upload_time = "2025-01-14T19:02:23.032Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819, upload-time = "2025-01-14T19:02:23.032Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/7f/2023c0a973f8823175c7e409fdbd306b275b0bb2723acf12ffade6ba5dbe/pyobjc_framework_AdSupport-11.0-py2.py3-none-any.whl", hash = "sha256:59161f5046def176d3aa6fdd6a05916029ca69ac69f836c67e0dd780a5efcf0f", size = 3334, upload_time = "2025-01-14T18:47:44.747Z" }, - { url = "https://files.pythonhosted.org/packages/cf/84/26c4275732952416603026888ca5462ed84372d412d0ccd7a1c750c01673/pyobjc_framework_AdSupport-11.0-py3-none-any.whl", hash = "sha256:91ba05eb5602911287bd04b0efefb7a485f9af255095b87c3e77bb7d1d1242ed", size = 3405, upload_time = "2025-01-14T18:47:45.767Z" }, + { url = "https://files.pythonhosted.org/packages/6f/7f/2023c0a973f8823175c7e409fdbd306b275b0bb2723acf12ffade6ba5dbe/pyobjc_framework_AdSupport-11.0-py2.py3-none-any.whl", hash = "sha256:59161f5046def176d3aa6fdd6a05916029ca69ac69f836c67e0dd780a5efcf0f", size = 3334, upload-time = "2025-01-14T18:47:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/cf/84/26c4275732952416603026888ca5462ed84372d412d0ccd7a1c750c01673/pyobjc_framework_AdSupport-11.0-py3-none-any.whl", hash = "sha256:91ba05eb5602911287bd04b0efefb7a485f9af255095b87c3e77bb7d1d1242ed", size = 3405, upload-time = "2025-01-14T18:47:45.767Z" }, ] [[package]] @@ -2111,10 +2111,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638, upload_time = "2025-01-14T19:02:25.1Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638, upload-time = "2025-01-14T19:02:25.1Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/4b/5e7f6a182129be6f229ee6c036d84359b46b0f5f695824315c47b19d3149/pyobjc_framework_AppleScriptKit-11.0-py2.py3-none-any.whl", hash = "sha256:e8acc5ca99f5123ec4e60cb356c7cc407d5fe533ca53e5fa341b51f65495973b", size = 4246, upload_time = "2025-01-14T18:47:59.508Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ce/7965604f553c91fbd5602e17057b0935c100542abaf76291921335b6f75c/pyobjc_framework_AppleScriptKit-11.0-py3-none-any.whl", hash = "sha256:92cffd943a4d17f684bb51245744e9d0bb8992b2967125845dfeab09d26fc624", size = 4317, upload_time = "2025-01-14T18:48:02.221Z" }, + { url = "https://files.pythonhosted.org/packages/97/4b/5e7f6a182129be6f229ee6c036d84359b46b0f5f695824315c47b19d3149/pyobjc_framework_AppleScriptKit-11.0-py2.py3-none-any.whl", hash = "sha256:e8acc5ca99f5123ec4e60cb356c7cc407d5fe533ca53e5fa341b51f65495973b", size = 4246, upload-time = "2025-01-14T18:47:59.508Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ce/7965604f553c91fbd5602e17057b0935c100542abaf76291921335b6f75c/pyobjc_framework_AppleScriptKit-11.0-py3-none-any.whl", hash = "sha256:92cffd943a4d17f684bb51245744e9d0bb8992b2967125845dfeab09d26fc624", size = 4317, upload-time = "2025-01-14T18:48:02.221Z" }, ] [[package]] @@ -2125,10 +2125,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675, upload_time = "2025-01-14T19:02:25.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675, upload-time = "2025-01-14T19:02:25.947Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/7d/b3e28759df060f26a31407282e789a1a321612afcee3871134fdac8dc75f/pyobjc_framework_AppleScriptObjC-11.0-py2.py3-none-any.whl", hash = "sha256:a4c8d417fdb64180a283eadf8ddb804ba7f9e3cef149216a11b65e1d3509c55b", size = 4347, upload_time = "2025-01-14T18:48:03.193Z" }, - { url = "https://files.pythonhosted.org/packages/0d/e7/c080a1cd77ce04e3bf4079a941105d3d670b9ba0fc91a54d4a1764bea02d/pyobjc_framework_AppleScriptObjC-11.0-py3-none-any.whl", hash = "sha256:681006b0cdf0279cd06b6d0f62b542b7f3b3b9b5d2391f7aa3798d8b355d67bf", size = 4416, upload_time = "2025-01-14T18:48:04.219Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7d/b3e28759df060f26a31407282e789a1a321612afcee3871134fdac8dc75f/pyobjc_framework_AppleScriptObjC-11.0-py2.py3-none-any.whl", hash = "sha256:a4c8d417fdb64180a283eadf8ddb804ba7f9e3cef149216a11b65e1d3509c55b", size = 4347, upload-time = "2025-01-14T18:48:03.193Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e7/c080a1cd77ce04e3bf4079a941105d3d670b9ba0fc91a54d4a1764bea02d/pyobjc_framework_AppleScriptObjC-11.0-py3-none-any.whl", hash = "sha256:681006b0cdf0279cd06b6d0f62b542b7f3b3b9b5d2391f7aa3798d8b355d67bf", size = 4416, upload-time = "2025-01-14T18:48:04.219Z" }, ] [[package]] @@ -2141,10 +2141,10 @@ dependencies = [ { name = "pyobjc-framework-coretext" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334, upload_time = "2025-01-14T19:02:26.828Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334, upload-time = "2025-01-14T19:02:26.828Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/37/3d4dc6c004aaeb67bd43f7261d7c169ff45b8fc0eefbc7ba8cd6b0c881bc/pyobjc_framework_ApplicationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61a99eef23abb704257310db4f5271137707e184768f6407030c01de4731b67b", size = 30846, upload_time = "2025-01-14T18:48:06.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/a9/7a45a67e126d32c61ea22ffd80e87ff7e05b4acf32bede6cce071fbfffc8/pyobjc_framework_ApplicationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5fbeb425897d6129471d451ec61a29ddd5b1386eb26b1dd49cb313e34616ee21", size = 30908, upload_time = "2025-01-14T18:48:07.177Z" }, + { url = "https://files.pythonhosted.org/packages/99/37/3d4dc6c004aaeb67bd43f7261d7c169ff45b8fc0eefbc7ba8cd6b0c881bc/pyobjc_framework_ApplicationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61a99eef23abb704257310db4f5271137707e184768f6407030c01de4731b67b", size = 30846, upload-time = "2025-01-14T18:48:06.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/a9/7a45a67e126d32c61ea22ffd80e87ff7e05b4acf32bede6cce071fbfffc8/pyobjc_framework_ApplicationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5fbeb425897d6129471d451ec61a29ddd5b1386eb26b1dd49cb313e34616ee21", size = 30908, upload-time = "2025-01-14T18:48:07.177Z" }, ] [[package]] @@ -2155,10 +2155,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385, upload_time = "2025-01-14T19:02:29.226Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385, upload-time = "2025-01-14T19:02:29.226Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/72/6e460cd763a3048c4d75769ed60a5af7832122b78224f710e40a9eb1c5cf/pyobjc_framework_AppTrackingTransparency-11.0-py2.py3-none-any.whl", hash = "sha256:1bf6d4f148d9f5d5befe90fcfd88ce988458a52719d53d5989b08e4fbed58864", size = 3805, upload_time = "2025-01-14T18:47:57.492Z" }, - { url = "https://files.pythonhosted.org/packages/33/cb/ef2622ee08349293aae6f81216cfee2423ad37d8a1d14ba4690b537d8850/pyobjc_framework_AppTrackingTransparency-11.0-py3-none-any.whl", hash = "sha256:347f876aea9d9f47d9fbf6dfa6d3f250ecd46f56a7c4616386327061e2ecc4e9", size = 3878, upload_time = "2025-01-14T18:47:58.595Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/6e460cd763a3048c4d75769ed60a5af7832122b78224f710e40a9eb1c5cf/pyobjc_framework_AppTrackingTransparency-11.0-py2.py3-none-any.whl", hash = "sha256:1bf6d4f148d9f5d5befe90fcfd88ce988458a52719d53d5989b08e4fbed58864", size = 3805, upload-time = "2025-01-14T18:47:57.492Z" }, + { url = "https://files.pythonhosted.org/packages/33/cb/ef2622ee08349293aae6f81216cfee2423ad37d8a1d14ba4690b537d8850/pyobjc_framework_AppTrackingTransparency-11.0-py3-none-any.whl", hash = "sha256:347f876aea9d9f47d9fbf6dfa6d3f250ecd46f56a7c4616386327061e2ecc4e9", size = 3878, upload-time = "2025-01-14T18:47:58.595Z" }, ] [[package]] @@ -2169,10 +2169,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418, upload_time = "2025-01-14T19:02:30.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418, upload-time = "2025-01-14T19:02:30.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/33/2ee33542febb40174d40ae8bbdf672b4e438a3fb41ba6a4d4a3e6800121b/pyobjc_framework_AudioVideoBridging-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d025e49ca6238be96d0a1c22942b548a8d445ef8eb71259b4769e119810f42c6", size = 10944, upload_time = "2025-01-14T18:48:11.978Z" }, - { url = "https://files.pythonhosted.org/packages/45/ea/db8295e17b0b544b06620e4019afcc76d7b743a8f03cb8a1024b2bc118ac/pyobjc_framework_AudioVideoBridging-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d414ecffeb23cddc8e64262af170e663c93e8d462d18aa7067d4584069967859", size = 10962, upload_time = "2025-01-14T18:48:12.953Z" }, + { url = "https://files.pythonhosted.org/packages/3c/33/2ee33542febb40174d40ae8bbdf672b4e438a3fb41ba6a4d4a3e6800121b/pyobjc_framework_AudioVideoBridging-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d025e49ca6238be96d0a1c22942b548a8d445ef8eb71259b4769e119810f42c6", size = 10944, upload-time = "2025-01-14T18:48:11.978Z" }, + { url = "https://files.pythonhosted.org/packages/45/ea/db8295e17b0b544b06620e4019afcc76d7b743a8f03cb8a1024b2bc118ac/pyobjc_framework_AudioVideoBridging-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d414ecffeb23cddc8e64262af170e663c93e8d462d18aa7067d4584069967859", size = 10962, upload-time = "2025-01-14T18:48:12.953Z" }, ] [[package]] @@ -2183,10 +2183,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036, upload_time = "2025-01-14T19:02:31.115Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036, upload-time = "2025-01-14T19:02:31.115Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/16/6246cf10e2d245f4018c02f351f8eb4cc93823f6e7e4dd584ab292cda786/pyobjc_framework_AuthenticationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84e3b23478cf8995883acfe6c1a24503c84caf2f8dbe540377fe19fb787ce9b2", size = 20079, upload_time = "2025-01-14T18:48:19.023Z" }, - { url = "https://files.pythonhosted.org/packages/b7/ca/81a55a0714e73695b536bfbcbf0f5ddf68da9485b468406f6ef887a04938/pyobjc_framework_AuthenticationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1779f72c264f749946fcbfba0575a985c1e297d426617739a533554dbf172f9a", size = 20105, upload_time = "2025-01-14T18:48:19.945Z" }, + { url = "https://files.pythonhosted.org/packages/45/16/6246cf10e2d245f4018c02f351f8eb4cc93823f6e7e4dd584ab292cda786/pyobjc_framework_AuthenticationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84e3b23478cf8995883acfe6c1a24503c84caf2f8dbe540377fe19fb787ce9b2", size = 20079, upload-time = "2025-01-14T18:48:19.023Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ca/81a55a0714e73695b536bfbcbf0f5ddf68da9485b468406f6ef887a04938/pyobjc_framework_AuthenticationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1779f72c264f749946fcbfba0575a985c1e297d426617739a533554dbf172f9a", size = 20105, upload-time = "2025-01-14T18:48:19.945Z" }, ] [[package]] @@ -2197,10 +2197,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420, upload_time = "2025-01-14T19:02:32.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420, upload-time = "2025-01-14T19:02:32.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3f/b593adce2f5b9f9427d784db56d8195adc2cfb340d4d3578914539a17faa/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:25f2db399eb0a47e345d0471c7af930f5a3be899ba6edb40bd9125719e4b526f", size = 9015, upload_time = "2025-01-14T18:48:25.686Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c3/b6b779d783dcf3667a2011d8af0d801f6639df9735cdc34c6e6b79822298/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b6433452d2c4cdb0eef16cc78a24ba9c61efb5bb04709ee10ca94b69119e889c", size = 9034, upload_time = "2025-01-14T18:48:26.58Z" }, + { url = "https://files.pythonhosted.org/packages/50/3f/b593adce2f5b9f9427d784db56d8195adc2cfb340d4d3578914539a17faa/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:25f2db399eb0a47e345d0471c7af930f5a3be899ba6edb40bd9125719e4b526f", size = 9015, upload-time = "2025-01-14T18:48:25.686Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c3/b6b779d783dcf3667a2011d8af0d801f6639df9735cdc34c6e6b79822298/pyobjc_framework_AutomaticAssessmentConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b6433452d2c4cdb0eef16cc78a24ba9c61efb5bb04709ee10ca94b69119e889c", size = 9034, upload-time = "2025-01-14T18:48:26.58Z" }, ] [[package]] @@ -2211,10 +2211,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905, upload_time = "2025-01-14T19:02:33.039Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905, upload-time = "2025-01-14T19:02:33.039Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/2b/bfe673491042849ad400bebf557b8047317757283b98ad9921fbb6f6cae9/pyobjc_framework_Automator-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:850f9641a54cc8d9a3d02c2d87a4e80aed2413b37aa6c26a7046088b77da5b42", size = 9811, upload_time = "2025-01-14T18:48:31.987Z" }, - { url = "https://files.pythonhosted.org/packages/13/00/e60db832c536fd354fab7e813ee781327358e6bcbc4cacbd9695dade7006/pyobjc_framework_Automator-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eb1b9b16873ec1d2f8af9a04ca1b2fcaa324ce4a1fada0d02fa239f6fecf773b", size = 9827, upload_time = "2025-01-14T18:48:32.958Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/bfe673491042849ad400bebf557b8047317757283b98ad9921fbb6f6cae9/pyobjc_framework_Automator-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:850f9641a54cc8d9a3d02c2d87a4e80aed2413b37aa6c26a7046088b77da5b42", size = 9811, upload-time = "2025-01-14T18:48:31.987Z" }, + { url = "https://files.pythonhosted.org/packages/13/00/e60db832c536fd354fab7e813ee781327358e6bcbc4cacbd9695dade7006/pyobjc_framework_Automator-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eb1b9b16873ec1d2f8af9a04ca1b2fcaa324ce4a1fada0d02fa239f6fecf773b", size = 9827, upload-time = "2025-01-14T18:48:32.958Z" }, ] [[package]] @@ -2228,10 +2228,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064, upload_time = "2025-01-14T19:02:35.757Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064, upload-time = "2025-01-14T19:02:35.757Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/b5/327654548fa210b4637350de016183fbb1f6f8f9213d2c6c9b492eb8b44c/pyobjc_framework_AVFoundation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87db350311c1d7e07d68036cdde3d01c09d97b8ba502241c0c1699d7a9c6f2e4", size = 71345, upload_time = "2025-01-14T18:47:04.589Z" }, - { url = "https://files.pythonhosted.org/packages/f5/36/e09b20f280953fa7be95a9266e5ad75f2e8b184cc39260c0537b3e60b534/pyobjc_framework_AVFoundation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6bb6f4be53c0fb42bee3f46cf0bb5396a8fd13f92d47a01f6b77037a1134f26b", size = 71314, upload_time = "2025-01-14T18:47:05.616Z" }, + { url = "https://files.pythonhosted.org/packages/44/b5/327654548fa210b4637350de016183fbb1f6f8f9213d2c6c9b492eb8b44c/pyobjc_framework_AVFoundation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87db350311c1d7e07d68036cdde3d01c09d97b8ba502241c0c1699d7a9c6f2e4", size = 71345, upload-time = "2025-01-14T18:47:04.589Z" }, + { url = "https://files.pythonhosted.org/packages/f5/36/e09b20f280953fa7be95a9266e5ad75f2e8b184cc39260c0537b3e60b534/pyobjc_framework_AVFoundation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6bb6f4be53c0fb42bee3f46cf0bb5396a8fd13f92d47a01f6b77037a1134f26b", size = 71314, upload-time = "2025-01-14T18:47:05.616Z" }, ] [[package]] @@ -2243,10 +2243,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311, upload_time = "2025-01-14T19:02:37.018Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311, upload-time = "2025-01-14T19:02:37.018Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/ae/aed1023150483a288922c447e1997f4f4e9d0460038e1a070a3a53b85c19/pyobjc_framework_AVKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16b5560860c1e13e692c677ad04d8e194d0b9931dd3f15e3df4dbd7217cc8ab1", size = 11960, upload_time = "2025-01-14T18:47:15.738Z" }, - { url = "https://files.pythonhosted.org/packages/01/f4/08684e5af2a2e8940e6411e96ef1d7ed1e51a121abb19c93c25c34969213/pyobjc_framework_AVKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f4da468b97bb7f356024e31647619cd1cd435b543e467209da0ee0abdfdc7121", size = 11969, upload_time = "2025-01-14T18:47:16.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/aed1023150483a288922c447e1997f4f4e9d0460038e1a070a3a53b85c19/pyobjc_framework_AVKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16b5560860c1e13e692c677ad04d8e194d0b9931dd3f15e3df4dbd7217cc8ab1", size = 11960, upload-time = "2025-01-14T18:47:15.738Z" }, + { url = "https://files.pythonhosted.org/packages/01/f4/08684e5af2a2e8940e6411e96ef1d7ed1e51a121abb19c93c25c34969213/pyobjc_framework_AVKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f4da468b97bb7f356024e31647619cd1cd435b543e467209da0ee0abdfdc7121", size = 11969, upload-time = "2025-01-14T18:47:16.602Z" }, ] [[package]] @@ -2257,10 +2257,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561, upload_time = "2025-01-14T19:02:38.157Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561, upload-time = "2025-01-14T19:02:38.157Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/9c/ea6924de09e13f858210d6dd934f00773b1e3db6af886c72841ed545560f/pyobjc_framework_AVRouting-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54e58cd0292f734aba035599f37a0c00f03761e9ff5cf53a0857cec7949bb39c", size = 8067, upload_time = "2025-01-14T18:47:26.354Z" }, - { url = "https://files.pythonhosted.org/packages/3f/92/774e10af5aba5742c4a2dd563fa819550d9caa755d2648b3cc87bbe30129/pyobjc_framework_AVRouting-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:779db3fb0048b22c5dcf5871930025c0fd93068f87946e8053f31a3366fa6fb0", size = 8078, upload_time = "2025-01-14T18:47:28.53Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9c/ea6924de09e13f858210d6dd934f00773b1e3db6af886c72841ed545560f/pyobjc_framework_AVRouting-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54e58cd0292f734aba035599f37a0c00f03761e9ff5cf53a0857cec7949bb39c", size = 8067, upload-time = "2025-01-14T18:47:26.354Z" }, + { url = "https://files.pythonhosted.org/packages/3f/92/774e10af5aba5742c4a2dd563fa819550d9caa755d2648b3cc87bbe30129/pyobjc_framework_AVRouting-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:779db3fb0048b22c5dcf5871930025c0fd93068f87946e8053f31a3366fa6fb0", size = 8078, upload-time = "2025-01-14T18:47:28.53Z" }, ] [[package]] @@ -2271,10 +2271,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658, upload_time = "2025-01-14T19:02:42.665Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658, upload-time = "2025-01-14T19:02:42.665Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/9d/bea4408649199340ec7ed154bbaa1942a0b0955006b3153088b3f35e6ff6/pyobjc_framework_BackgroundAssets-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:812bcc4eaf71c1cc42e94edc2b5ad0414d16cfe1da5c421edd9382417d625f06", size = 9499, upload_time = "2025-01-14T18:48:39.156Z" }, - { url = "https://files.pythonhosted.org/packages/bd/79/726c14fd26553c8bbe8b2ed55caa45d89093e2e85b45c1b598dd04ea7589/pyobjc_framework_BackgroundAssets-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:96b3fc40c514867d4a0b3ad4d256bc5134d789e22fa306a6b21e49ecadc51698", size = 9521, upload_time = "2025-01-14T18:48:40.063Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9d/bea4408649199340ec7ed154bbaa1942a0b0955006b3153088b3f35e6ff6/pyobjc_framework_BackgroundAssets-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:812bcc4eaf71c1cc42e94edc2b5ad0414d16cfe1da5c421edd9382417d625f06", size = 9499, upload-time = "2025-01-14T18:48:39.156Z" }, + { url = "https://files.pythonhosted.org/packages/bd/79/726c14fd26553c8bbe8b2ed55caa45d89093e2e85b45c1b598dd04ea7589/pyobjc_framework_BackgroundAssets-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:96b3fc40c514867d4a0b3ad4d256bc5134d789e22fa306a6b21e49ecadc51698", size = 9521, upload-time = "2025-01-14T18:48:40.063Z" }, ] [[package]] @@ -2288,10 +2288,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352, upload_time = "2025-01-14T19:02:45.499Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352, upload-time = "2025-01-14T19:02:45.499Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/6d/6aa929d4993453817523db9c82a4e6e2cce7104fa59e29ee857f9e926b0d/pyobjc_framework_BrowserEngineKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:58494bc3ccc21a63751b7c9f8788d0240c3f1aad84cf221c0e42c9764a069ba4", size = 10913, upload_time = "2025-01-14T18:48:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2f/dd18f7ff9438ad4612febfbdb2e4bded37033347b9f0e1355df76f2c5213/pyobjc_framework_BrowserEngineKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0925edfd60a24f53819cfd11f07926fd42bc0fbeb7a4982998a08742e859dbff", size = 10933, upload_time = "2025-01-14T18:48:45.673Z" }, + { url = "https://files.pythonhosted.org/packages/da/6d/6aa929d4993453817523db9c82a4e6e2cce7104fa59e29ee857f9e926b0d/pyobjc_framework_BrowserEngineKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:58494bc3ccc21a63751b7c9f8788d0240c3f1aad84cf221c0e42c9764a069ba4", size = 10913, upload-time = "2025-01-14T18:48:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/dd18f7ff9438ad4612febfbdb2e4bded37033347b9f0e1355df76f2c5213/pyobjc_framework_BrowserEngineKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0925edfd60a24f53819cfd11f07926fd42bc0fbeb7a4982998a08742e859dbff", size = 10933, upload-time = "2025-01-14T18:48:45.673Z" }, ] [[package]] @@ -2302,10 +2302,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953, upload_time = "2025-01-14T19:02:46.378Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953, upload-time = "2025-01-14T19:02:46.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/5b/d7313368ea4056092400c7a4ed5c705d3d21a443641d98b140054edbd930/pyobjc_framework_BusinessChat-11.0-py2.py3-none-any.whl", hash = "sha256:1f732fdace31d2abdd14b3054f27a5e0f4591c7e1bef069b6aeb4f9c8d9ec487", size = 3408, upload_time = "2025-01-14T18:48:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e6/c82e2eb2b4ad4407f1ada6d41ef583eb211cce88ffcc2e05c826760f721d/pyobjc_framework_BusinessChat-11.0-py3-none-any.whl", hash = "sha256:47a2e4da9b061daa89a6367cb0e6bb8cdea0627379dd6d5095a8fd20243d8613", size = 3477, upload_time = "2025-01-14T18:48:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/d4/5b/d7313368ea4056092400c7a4ed5c705d3d21a443641d98b140054edbd930/pyobjc_framework_BusinessChat-11.0-py2.py3-none-any.whl", hash = "sha256:1f732fdace31d2abdd14b3054f27a5e0f4591c7e1bef069b6aeb4f9c8d9ec487", size = 3408, upload-time = "2025-01-14T18:48:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e6/c82e2eb2b4ad4407f1ada6d41ef583eb211cce88ffcc2e05c826760f721d/pyobjc_framework_BusinessChat-11.0-py3-none-any.whl", hash = "sha256:47a2e4da9b061daa89a6367cb0e6bb8cdea0627379dd6d5095a8fd20243d8613", size = 3477, upload-time = "2025-01-14T18:48:52.723Z" }, ] [[package]] @@ -2316,10 +2316,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475, upload_time = "2025-01-14T19:02:48.544Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475, upload-time = "2025-01-14T19:02:48.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/e1/02bda98aae43957943adb09700265603f8ff8ff2197e57b082237a8e1a8f/pyobjc_framework_CalendarStore-11.0-py2.py3-none-any.whl", hash = "sha256:67ddc18c96bba42118fc92f1117b053c58c8888edb74193f0be67a10051cc9e2", size = 5183, upload_time = "2025-01-14T18:49:01.649Z" }, - { url = "https://files.pythonhosted.org/packages/a2/5b/922df21b738e8d349df27b2a73eaf8bba93c84c8c4d0d133fdd5de2ff236/pyobjc_framework_CalendarStore-11.0-py3-none-any.whl", hash = "sha256:9b310fe66ac12e0feb7c8e3166034bec357a45f7f8b8916e93eddc6f199d08c8", size = 5251, upload_time = "2025-01-14T18:49:03.224Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e1/02bda98aae43957943adb09700265603f8ff8ff2197e57b082237a8e1a8f/pyobjc_framework_CalendarStore-11.0-py2.py3-none-any.whl", hash = "sha256:67ddc18c96bba42118fc92f1117b053c58c8888edb74193f0be67a10051cc9e2", size = 5183, upload-time = "2025-01-14T18:49:01.649Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5b/922df21b738e8d349df27b2a73eaf8bba93c84c8c4d0d133fdd5de2ff236/pyobjc_framework_CalendarStore-11.0-py3-none-any.whl", hash = "sha256:9b310fe66ac12e0feb7c8e3166034bec357a45f7f8b8916e93eddc6f199d08c8", size = 5251, upload-time = "2025-01-14T18:49:03.224Z" }, ] [[package]] @@ -2330,10 +2330,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720, upload_time = "2025-01-14T19:02:50.697Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720, upload-time = "2025-01-14T19:02:50.697Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/86/8d7dc24702ae810b6230d8b2cebb1c31e12abc31507095b1a9655715c921/pyobjc_framework_CallKit-11.0-py2.py3-none-any.whl", hash = "sha256:f19d94b61ecd981f4691fd244f536f947687b872ac793ccc2b3122b3854e887a", size = 5248, upload_time = "2025-01-14T18:49:05.438Z" }, - { url = "https://files.pythonhosted.org/packages/25/bd/ff89f7e5438c767fc43f603bee42a447315be48a09f64b9aa4da719ecdfc/pyobjc_framework_CallKit-11.0-py3-none-any.whl", hash = "sha256:95394b7f7a50916debe4f7a884ce9135d11733a14e07a8c502171e77bd0087a4", size = 5314, upload_time = "2025-01-14T18:49:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/22/86/8d7dc24702ae810b6230d8b2cebb1c31e12abc31507095b1a9655715c921/pyobjc_framework_CallKit-11.0-py2.py3-none-any.whl", hash = "sha256:f19d94b61ecd981f4691fd244f536f947687b872ac793ccc2b3122b3854e887a", size = 5248, upload-time = "2025-01-14T18:49:05.438Z" }, + { url = "https://files.pythonhosted.org/packages/25/bd/ff89f7e5438c767fc43f603bee42a447315be48a09f64b9aa4da719ecdfc/pyobjc_framework_CallKit-11.0-py3-none-any.whl", hash = "sha256:95394b7f7a50916debe4f7a884ce9135d11733a14e07a8c502171e77bd0087a4", size = 5314, upload-time = "2025-01-14T18:49:06.459Z" }, ] [[package]] @@ -2344,10 +2344,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538, upload_time = "2025-01-14T19:02:51.62Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538, upload-time = "2025-01-14T19:02:51.62Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/fb/e5724934c3a2bbed4fbda4230e15a8b7b86313b39491876647300cb4fb11/pyobjc_framework_Carbon-11.0-py2.py3-none-any.whl", hash = "sha256:beef5095269d8e5427e09f9687963515c1b79fbf6927ff756a8414445892987d", size = 4700, upload_time = "2025-01-14T18:49:07.341Z" }, - { url = "https://files.pythonhosted.org/packages/1a/3d/b53c2d8949067f3f45491e250620e437569f1b4e6a028f2f5e721726283e/pyobjc_framework_Carbon-11.0-py3-none-any.whl", hash = "sha256:9a269042e8f5705897ac64d2b48515ba055462c88460cf140f5d8d4b8c806a42", size = 4768, upload_time = "2025-01-14T18:49:10.256Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/e5724934c3a2bbed4fbda4230e15a8b7b86313b39491876647300cb4fb11/pyobjc_framework_Carbon-11.0-py2.py3-none-any.whl", hash = "sha256:beef5095269d8e5427e09f9687963515c1b79fbf6927ff756a8414445892987d", size = 4700, upload-time = "2025-01-14T18:49:07.341Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3d/b53c2d8949067f3f45491e250620e437569f1b4e6a028f2f5e721726283e/pyobjc_framework_Carbon-11.0-py3-none-any.whl", hash = "sha256:9a269042e8f5705897ac64d2b48515ba055462c88460cf140f5d8d4b8c806a42", size = 4768, upload-time = "2025-01-14T18:49:10.256Z" }, ] [[package]] @@ -2358,10 +2358,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532, upload_time = "2025-01-14T19:02:52.777Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532, upload-time = "2025-01-14T19:02:52.777Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/85/11047cfe2d31c242694d780783f0dea81d73cbb09929c7d4b918ce2d29bf/pyobjc_framework_CFNetwork-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e6905c86ccb5608f4153aacb931758ad39af8b708fcebb497431f9741f39e6d", size = 18988, upload_time = "2025-01-14T18:48:54.869Z" }, - { url = "https://files.pythonhosted.org/packages/3e/6e/7d90c329030e7dd6ebbec217434820ff6158a3af9906e2abbb43e9b685d6/pyobjc_framework_CFNetwork-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f61010503073e3518e29d440079a7c0b40aef91be6d3c2032e492c21bada80b", size = 19144, upload_time = "2025-01-14T18:48:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/d9/85/11047cfe2d31c242694d780783f0dea81d73cbb09929c7d4b918ce2d29bf/pyobjc_framework_CFNetwork-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e6905c86ccb5608f4153aacb931758ad39af8b708fcebb497431f9741f39e6d", size = 18988, upload-time = "2025-01-14T18:48:54.869Z" }, + { url = "https://files.pythonhosted.org/packages/3e/6e/7d90c329030e7dd6ebbec217434820ff6158a3af9906e2abbb43e9b685d6/pyobjc_framework_CFNetwork-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f61010503073e3518e29d440079a7c0b40aef91be6d3c2032e492c21bada80b", size = 19144, upload-time = "2025-01-14T18:48:55.864Z" }, ] [[package]] @@ -2375,10 +2375,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740, upload_time = "2025-01-14T19:02:54.95Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740, upload-time = "2025-01-14T19:02:54.95Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/cf/a60e131bddf5cced32a3c0050d264f2255d63c45be398cede1db03ea8b51/pyobjc_framework_Cinematic-11.0-py2.py3-none-any.whl", hash = "sha256:281721969978d726ded9bae38c4acd6713495c399025ff2b4179fc02ec68b336", size = 4508, upload_time = "2025-01-14T18:49:11.202Z" }, - { url = "https://files.pythonhosted.org/packages/09/a8/4ea347c1fc5774e2bbe7bb688fc625d583103d1e212f7b896ed19d14844b/pyobjc_framework_Cinematic-11.0-py3-none-any.whl", hash = "sha256:3a24f3528d7f77637f51fd1862cc8c79e4d0da4ba6fd3dd02b54adddec365826", size = 4580, upload_time = "2025-01-14T18:49:12.251Z" }, + { url = "https://files.pythonhosted.org/packages/55/cf/a60e131bddf5cced32a3c0050d264f2255d63c45be398cede1db03ea8b51/pyobjc_framework_Cinematic-11.0-py2.py3-none-any.whl", hash = "sha256:281721969978d726ded9bae38c4acd6713495c399025ff2b4179fc02ec68b336", size = 4508, upload-time = "2025-01-14T18:49:11.202Z" }, + { url = "https://files.pythonhosted.org/packages/09/a8/4ea347c1fc5774e2bbe7bb688fc625d583103d1e212f7b896ed19d14844b/pyobjc_framework_Cinematic-11.0-py3-none-any.whl", hash = "sha256:3a24f3528d7f77637f51fd1862cc8c79e4d0da4ba6fd3dd02b54adddec365826", size = 4580, upload-time = "2025-01-14T18:49:12.251Z" }, ] [[package]] @@ -2389,10 +2389,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301, upload_time = "2025-01-14T19:02:55.779Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301, upload-time = "2025-01-14T19:02:55.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/77/2e31bcf1e9b63f6723c01329c1191ac163e79b0f548b7cd92414115c26ff/pyobjc_framework_ClassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:723a07591e1e40380c339b58033e8491e58be4080c0f77a26be0728f1c5025c8", size = 8776, upload_time = "2025-01-14T18:49:14.05Z" }, - { url = "https://files.pythonhosted.org/packages/68/87/f566c4f1ffd1e383c7b38cd22753dfef0863f30bfdb0b3c5102293057fc2/pyobjc_framework_ClassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7c7ff2eb8a9d87cb69618668e96c41ed9467fd4b4a8fef517c49923c0f6418e6", size = 8794, upload_time = "2025-01-14T18:49:14.985Z" }, + { url = "https://files.pythonhosted.org/packages/a8/77/2e31bcf1e9b63f6723c01329c1191ac163e79b0f548b7cd92414115c26ff/pyobjc_framework_ClassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:723a07591e1e40380c339b58033e8491e58be4080c0f77a26be0728f1c5025c8", size = 8776, upload-time = "2025-01-14T18:49:14.05Z" }, + { url = "https://files.pythonhosted.org/packages/68/87/f566c4f1ffd1e383c7b38cd22753dfef0863f30bfdb0b3c5102293057fc2/pyobjc_framework_ClassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7c7ff2eb8a9d87cb69618668e96c41ed9467fd4b4a8fef517c49923c0f6418e6", size = 8794, upload-time = "2025-01-14T18:49:14.985Z" }, ] [[package]] @@ -2406,10 +2406,10 @@ dependencies = [ { name = "pyobjc-framework-coredata" }, { name = "pyobjc-framework-corelocation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034, upload_time = "2025-01-14T19:02:56.769Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034, upload-time = "2025-01-14T19:02:56.769Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/db/9f914422be88eb2c917d67aebac9dde2e272ea1b510ca1e0db17a09db125/pyobjc_framework_CloudKit-11.0-py2.py3-none-any.whl", hash = "sha256:10cb153d7185dd260d21596f75fca8502236f6afd8e72e866cff8acd9c025f14", size = 10785, upload_time = "2025-01-14T18:49:21.369Z" }, - { url = "https://files.pythonhosted.org/packages/53/73/239581763a1bd56475ebd9bdde52a79cf0b6cac20b3d4442283b1ef8705c/pyobjc_framework_CloudKit-11.0-py3-none-any.whl", hash = "sha256:b2376d92d5822ce7e4feefcffdc3f4d1d230929f1735793da6d36b52b161b552", size = 10854, upload_time = "2025-01-14T18:49:23.612Z" }, + { url = "https://files.pythonhosted.org/packages/c2/db/9f914422be88eb2c917d67aebac9dde2e272ea1b510ca1e0db17a09db125/pyobjc_framework_CloudKit-11.0-py2.py3-none-any.whl", hash = "sha256:10cb153d7185dd260d21596f75fca8502236f6afd8e72e866cff8acd9c025f14", size = 10785, upload-time = "2025-01-14T18:49:21.369Z" }, + { url = "https://files.pythonhosted.org/packages/53/73/239581763a1bd56475ebd9bdde52a79cf0b6cac20b3d4442283b1ef8705c/pyobjc_framework_CloudKit-11.0-py3-none-any.whl", hash = "sha256:b2376d92d5822ce7e4feefcffdc3f4d1d230929f1735793da6d36b52b161b552", size = 10854, upload-time = "2025-01-14T18:49:23.612Z" }, ] [[package]] @@ -2419,10 +2419,10 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyobjc-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848, upload_time = "2025-01-14T19:03:00.125Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848, upload-time = "2025-01-14T19:03:00.125Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/97/81fd41ad90e9c241172110aa635a6239d56f50d75923aaedbbe351828580/pyobjc_framework_Cocoa-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3ea7be6e6dd801b297440de02d312ba3fa7fd3c322db747ae1cb237e975f5d33", size = 385534, upload_time = "2025-01-14T18:49:27.898Z" }, - { url = "https://files.pythonhosted.org/packages/5b/8d/0e2558447c26b3ba64f7c9776a5a6c9d2ae8abf9d34308b174ae0934402e/pyobjc_framework_Cocoa-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:280a577b83c68175a28b2b7138d1d2d3111f2b2b66c30e86f81a19c2b02eae71", size = 385811, upload_time = "2025-01-14T18:49:29.259Z" }, + { url = "https://files.pythonhosted.org/packages/23/97/81fd41ad90e9c241172110aa635a6239d56f50d75923aaedbbe351828580/pyobjc_framework_Cocoa-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3ea7be6e6dd801b297440de02d312ba3fa7fd3c322db747ae1cb237e975f5d33", size = 385534, upload-time = "2025-01-14T18:49:27.898Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8d/0e2558447c26b3ba64f7c9776a5a6c9d2ae8abf9d34308b174ae0934402e/pyobjc_framework_Cocoa-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:280a577b83c68175a28b2b7138d1d2d3111f2b2b66c30e86f81a19c2b02eae71", size = 385811, upload-time = "2025-01-14T18:49:29.259Z" }, ] [[package]] @@ -2433,10 +2433,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084, upload_time = "2025-01-14T19:03:01.98Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084, upload-time = "2025-01-14T19:03:01.98Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ee/95883b6fbdbeecd99217c50c415ca024db5beb1923b935189a113412203d/pyobjc_framework_Collaboration-11.0-py2.py3-none-any.whl", hash = "sha256:acf11e584e21f6342e6d7be1675f36c92804082c29d2f373d1ca623a63959e76", size = 4807, upload_time = "2025-01-14T18:49:37.145Z" }, - { url = "https://files.pythonhosted.org/packages/c0/e5/d3ba7e3e3f306ba93c021c083287c668704d84605e0f788583abcfde815f/pyobjc_framework_Collaboration-11.0-py3-none-any.whl", hash = "sha256:e7789503ea9280ba365ce2c4e6c7c8b13dfa9174b2ecf9d174bbf9773f25f97a", size = 4876, upload_time = "2025-01-14T18:49:39.887Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ee/95883b6fbdbeecd99217c50c415ca024db5beb1923b935189a113412203d/pyobjc_framework_Collaboration-11.0-py2.py3-none-any.whl", hash = "sha256:acf11e584e21f6342e6d7be1675f36c92804082c29d2f373d1ca623a63959e76", size = 4807, upload-time = "2025-01-14T18:49:37.145Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e5/d3ba7e3e3f306ba93c021c083287c668704d84605e0f788583abcfde815f/pyobjc_framework_Collaboration-11.0-py3-none-any.whl", hash = "sha256:e7789503ea9280ba365ce2c4e6c7c8b13dfa9174b2ecf9d174bbf9773f25f97a", size = 4876, upload-time = "2025-01-14T18:49:39.887Z" }, ] [[package]] @@ -2447,10 +2447,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249, upload_time = "2025-01-14T19:03:02.887Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249, upload-time = "2025-01-14T19:03:02.887Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/16/d806b5c3ff5bf8f46a4770f89b2076d2596c1301c851c60bb43aea457cd3/pyobjc_framework_ColorSync-11.0-py2.py3-none-any.whl", hash = "sha256:24f5c3e0987bfdfe6a0de36f2f908e30ea52000eb649db7b0373928140518163", size = 5916, upload_time = "2025-01-14T18:49:41.273Z" }, - { url = "https://files.pythonhosted.org/packages/06/18/777bad37aab42f75d2ef2efb9240308c15c33b3a0636278111ec6c5df550/pyobjc_framework_ColorSync-11.0-py3-none-any.whl", hash = "sha256:cbee2211f64be927eb4e4717bf6e275bf28954ed86e4a4655d367c30f856494d", size = 5987, upload_time = "2025-01-14T18:49:42.286Z" }, + { url = "https://files.pythonhosted.org/packages/78/16/d806b5c3ff5bf8f46a4770f89b2076d2596c1301c851c60bb43aea457cd3/pyobjc_framework_ColorSync-11.0-py2.py3-none-any.whl", hash = "sha256:24f5c3e0987bfdfe6a0de36f2f908e30ea52000eb649db7b0373928140518163", size = 5916, upload-time = "2025-01-14T18:49:41.273Z" }, + { url = "https://files.pythonhosted.org/packages/06/18/777bad37aab42f75d2ef2efb9240308c15c33b3a0636278111ec6c5df550/pyobjc_framework_ColorSync-11.0-py3-none-any.whl", hash = "sha256:cbee2211f64be927eb4e4717bf6e275bf28954ed86e4a4655d367c30f856494d", size = 5987, upload-time = "2025-01-14T18:49:42.286Z" }, ] [[package]] @@ -2461,10 +2461,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253, upload_time = "2025-01-14T19:03:03.743Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253, upload-time = "2025-01-14T19:03:03.743Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/4f/b7a7b08535015494940a62fd63825eccf4cace7f8ca87050f0837470eca8/pyobjc_framework_Contacts-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b16758fc1edc40f0ec288d67b7e39b59609fb1df2523f4362c958d150619dbe5", size = 11880, upload_time = "2025-01-14T18:49:44.316Z" }, - { url = "https://files.pythonhosted.org/packages/07/4b/0d2b41a32b6432182548cb84bb6b1c3228a7ff428ea15dfaf812b39c028f/pyobjc_framework_Contacts-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:80972851e2163b94d82fd4b0d9801790ad420dffa91a37c90fa2949031881c02", size = 11957, upload_time = "2025-01-14T18:49:46.952Z" }, + { url = "https://files.pythonhosted.org/packages/91/4f/b7a7b08535015494940a62fd63825eccf4cace7f8ca87050f0837470eca8/pyobjc_framework_Contacts-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b16758fc1edc40f0ec288d67b7e39b59609fb1df2523f4362c958d150619dbe5", size = 11880, upload-time = "2025-01-14T18:49:44.316Z" }, + { url = "https://files.pythonhosted.org/packages/07/4b/0d2b41a32b6432182548cb84bb6b1c3228a7ff428ea15dfaf812b39c028f/pyobjc_framework_Contacts-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:80972851e2163b94d82fd4b0d9801790ad420dffa91a37c90fa2949031881c02", size = 11957, upload-time = "2025-01-14T18:49:46.952Z" }, ] [[package]] @@ -2476,10 +2476,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-contacts" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486, upload_time = "2025-01-14T19:03:04.72Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486, upload-time = "2025-01-14T19:03:04.72Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/47/b1dbe48c64e2d061bf8b4ee532413b97e6c5748fdba43598a30421086fcc/pyobjc_framework_ContactsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd3efaf3f67e92704f41927c5de06ccc4aa9daa09865cba7ac476da9c6e1c3c2", size = 7734, upload_time = "2025-01-14T18:49:52.765Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c5/465656c744301bfb7640e4077c57170d245843311e0e66702b53295e2534/pyobjc_framework_ContactsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9c85dccdf518a0ac80c627daca32d56a4636e3f118359579de51a428e85ba7", size = 7739, upload_time = "2025-01-14T18:49:55.507Z" }, + { url = "https://files.pythonhosted.org/packages/fc/47/b1dbe48c64e2d061bf8b4ee532413b97e6c5748fdba43598a30421086fcc/pyobjc_framework_ContactsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd3efaf3f67e92704f41927c5de06ccc4aa9daa09865cba7ac476da9c6e1c3c2", size = 7734, upload-time = "2025-01-14T18:49:52.765Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c5/465656c744301bfb7640e4077c57170d245843311e0e66702b53295e2534/pyobjc_framework_ContactsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9c85dccdf518a0ac80c627daca32d56a4636e3f118359579de51a428e85ba7", size = 7739, upload-time = "2025-01-14T18:49:55.507Z" }, ] [[package]] @@ -2490,10 +2490,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507, upload_time = "2025-01-14T19:03:05.612Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507, upload-time = "2025-01-14T19:03:05.612Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/f8/6f583376d2ef6a6123141d310f7f7e3e93ba9129ffbbc6eb26e25c4289c5/pyobjc_framework_CoreAudio-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:143cd44d5c069aee1abc5a88794e9531250b9fe70a98f6a08e493184dcf64b3e", size = 35750, upload_time = "2025-01-14T18:50:00.665Z" }, - { url = "https://files.pythonhosted.org/packages/df/14/b33556c06529a3c54853c41c5163e30a3fb9b2ae920e0c65a42ccd82e279/pyobjc_framework_CoreAudio-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d26eac5bc325bf046fc0bfdaa3322ddc828690dab726275f1c4c118bb888cc00", size = 36584, upload_time = "2025-01-14T18:50:01.806Z" }, + { url = "https://files.pythonhosted.org/packages/78/f8/6f583376d2ef6a6123141d310f7f7e3e93ba9129ffbbc6eb26e25c4289c5/pyobjc_framework_CoreAudio-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:143cd44d5c069aee1abc5a88794e9531250b9fe70a98f6a08e493184dcf64b3e", size = 35750, upload-time = "2025-01-14T18:50:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/df/14/b33556c06529a3c54853c41c5163e30a3fb9b2ae920e0c65a42ccd82e279/pyobjc_framework_CoreAudio-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d26eac5bc325bf046fc0bfdaa3322ddc828690dab726275f1c4c118bb888cc00", size = 36584, upload-time = "2025-01-14T18:50:01.806Z" }, ] [[package]] @@ -2505,10 +2505,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coreaudio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450, upload_time = "2025-01-14T19:03:06.681Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450, upload-time = "2025-01-14T19:03:06.681Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/b9/d75a4da2d6a3cb75bafd363c447d45e134fe78a340dee408423a40c04aac/pyobjc_framework_CoreAudioKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dbf01f2625689b392c2ba02f3ab8186c914d84d6bd896bdef5181a15a9463df", size = 7192, upload_time = "2025-01-14T18:50:09.524Z" }, - { url = "https://files.pythonhosted.org/packages/46/1f/5c15023665cc0476cdd7cbc054d5b06489fc09990f068768ed2fda8a02a2/pyobjc_framework_CoreAudioKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ccf2d92052a446d1d38bfd7eaa1dcd2451d59c37e73070a9a1fee394a532d9d", size = 7214, upload_time = "2025-01-14T18:50:10.399Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b9/d75a4da2d6a3cb75bafd363c447d45e134fe78a340dee408423a40c04aac/pyobjc_framework_CoreAudioKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dbf01f2625689b392c2ba02f3ab8186c914d84d6bd896bdef5181a15a9463df", size = 7192, upload-time = "2025-01-14T18:50:09.524Z" }, + { url = "https://files.pythonhosted.org/packages/46/1f/5c15023665cc0476cdd7cbc054d5b06489fc09990f068768ed2fda8a02a2/pyobjc_framework_CoreAudioKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ccf2d92052a446d1d38bfd7eaa1dcd2451d59c37e73070a9a1fee394a532d9d", size = 7214, upload-time = "2025-01-14T18:50:10.399Z" }, ] [[package]] @@ -2519,10 +2519,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797, upload_time = "2025-01-14T19:03:07.584Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797, upload-time = "2025-01-14T19:03:07.584Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/a8/df866e8a84fd33d29af1ee383f13715bbd98ad67d5795dfb276a3887560f/pyobjc_framework_CoreBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:044069d63447554ba2c65cb1bf58d489d14ea279344810386392e583a2e611ef", size = 13683, upload_time = "2025-01-14T18:50:16.231Z" }, - { url = "https://files.pythonhosted.org/packages/44/fa/ad2165bc93c9d3fb174a0d8d5a4db3a7dfcf4dcaeca7913d59748ef62fdb/pyobjc_framework_CoreBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bae8f909512d014eed85f80deae671185af4bb5a671fba19f85c7b4c973b61bb", size = 13713, upload_time = "2025-01-14T18:50:17.122Z" }, + { url = "https://files.pythonhosted.org/packages/53/a8/df866e8a84fd33d29af1ee383f13715bbd98ad67d5795dfb276a3887560f/pyobjc_framework_CoreBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:044069d63447554ba2c65cb1bf58d489d14ea279344810386392e583a2e611ef", size = 13683, upload-time = "2025-01-14T18:50:16.231Z" }, + { url = "https://files.pythonhosted.org/packages/44/fa/ad2165bc93c9d3fb174a0d8d5a4db3a7dfcf4dcaeca7913d59748ef62fdb/pyobjc_framework_CoreBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bae8f909512d014eed85f80deae671185af4bb5a671fba19f85c7b4c973b61bb", size = 13713, upload-time = "2025-01-14T18:50:17.122Z" }, ] [[package]] @@ -2533,10 +2533,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029, upload_time = "2025-01-14T19:03:08.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029, upload-time = "2025-01-14T19:03:08.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/b0/32c23ee168e5081391daa8737fddde79670b083e948dffb8d74308f1dd43/pyobjc_framework_CoreData-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74ac5e7658df10544708f6017a8823a100fbe41dc3aa9f61bf2fd4f8773c3dd7", size = 16194, upload_time = "2025-01-14T18:50:22.924Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9e/39ca8124c6d87dc6fa85bcf850a2c23a062a408a26300062041c10363a3f/pyobjc_framework_CoreData-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c23b8c9106b0ec6f43aca80d2b2e0b0cc8fcb4ba78db4ae3c1f39a67464357d7", size = 16208, upload_time = "2025-01-14T18:50:23.838Z" }, + { url = "https://files.pythonhosted.org/packages/86/b0/32c23ee168e5081391daa8737fddde79670b083e948dffb8d74308f1dd43/pyobjc_framework_CoreData-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74ac5e7658df10544708f6017a8823a100fbe41dc3aa9f61bf2fd4f8773c3dd7", size = 16194, upload-time = "2025-01-14T18:50:22.924Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9e/39ca8124c6d87dc6fa85bcf850a2c23a062a408a26300062041c10363a3f/pyobjc_framework_CoreData-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c23b8c9106b0ec6f43aca80d2b2e0b0cc8fcb4ba78db4ae3c1f39a67464357d7", size = 16208, upload-time = "2025-01-14T18:50:23.838Z" }, ] [[package]] @@ -2547,10 +2547,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956, upload_time = "2025-01-14T19:03:09.753Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956, upload-time = "2025-01-14T19:03:09.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/16/16d4365c8da1f708e145500237a3cdbbdde3e83b7f3f8673b038efac03b9/pyobjc_framework_CoreHaptics-11.0-py2.py3-none-any.whl", hash = "sha256:ff1d8f58dd3b29287dfad16a60bb45706c91f1910e400b632cb664eb2e56588b", size = 5307, upload_time = "2025-01-14T18:50:33.074Z" }, - { url = "https://files.pythonhosted.org/packages/12/72/b9fca92b3704af8f5f3b5507d0d9f3d0f5eb16605664de669f4468858627/pyobjc_framework_CoreHaptics-11.0-py3-none-any.whl", hash = "sha256:33f7a767efe6867fa6821ad73872ea88aec44650a22217bcdc9c1ec7c41fd9dc", size = 5377, upload_time = "2025-01-14T18:50:34.484Z" }, + { url = "https://files.pythonhosted.org/packages/96/16/16d4365c8da1f708e145500237a3cdbbdde3e83b7f3f8673b038efac03b9/pyobjc_framework_CoreHaptics-11.0-py2.py3-none-any.whl", hash = "sha256:ff1d8f58dd3b29287dfad16a60bb45706c91f1910e400b632cb664eb2e56588b", size = 5307, upload-time = "2025-01-14T18:50:33.074Z" }, + { url = "https://files.pythonhosted.org/packages/12/72/b9fca92b3704af8f5f3b5507d0d9f3d0f5eb16605664de669f4468858627/pyobjc_framework_CoreHaptics-11.0-py3-none-any.whl", hash = "sha256:33f7a767efe6867fa6821ad73872ea88aec44650a22217bcdc9c1ec7c41fd9dc", size = 5377, upload-time = "2025-01-14T18:50:34.484Z" }, ] [[package]] @@ -2561,10 +2561,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955, upload_time = "2025-01-14T19:03:10.707Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955, upload-time = "2025-01-14T19:03:10.707Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/99/c7844f6e583f4764c6fab4a5b5ad9e949c6fce8c30f95226118bead41e01/pyobjc_framework_CoreLocation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:046f211a23de55364c8553cfd660dc5adeff28af4d25f5ed9b5a8bfa83266b4d", size = 13075, upload_time = "2025-01-14T18:50:37.789Z" }, - { url = "https://files.pythonhosted.org/packages/88/6b/bb4fbcd259404fb60fdbfecef3c426dc23da5a0f0bc5bf96a4169b047478/pyobjc_framework_CoreLocation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9bca9974f143bc9e93bd7ec4ef91655964d8ad0ca5ffccc8404fb6f098fa08dc", size = 13076, upload_time = "2025-01-14T18:50:38.693Z" }, + { url = "https://files.pythonhosted.org/packages/10/99/c7844f6e583f4764c6fab4a5b5ad9e949c6fce8c30f95226118bead41e01/pyobjc_framework_CoreLocation-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:046f211a23de55364c8553cfd660dc5adeff28af4d25f5ed9b5a8bfa83266b4d", size = 13075, upload-time = "2025-01-14T18:50:37.789Z" }, + { url = "https://files.pythonhosted.org/packages/88/6b/bb4fbcd259404fb60fdbfecef3c426dc23da5a0f0bc5bf96a4169b047478/pyobjc_framework_CoreLocation-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9bca9974f143bc9e93bd7ec4ef91655964d8ad0ca5ffccc8404fb6f098fa08dc", size = 13076, upload-time = "2025-01-14T18:50:38.693Z" }, ] [[package]] @@ -2575,10 +2575,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161, upload_time = "2025-01-14T19:03:12.291Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161, upload-time = "2025-01-14T19:03:12.291Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/b3/7baca352ddd7256840a4eb8f38fda39bc2e023b222b86d11c1a77cc0a8fa/pyobjc_framework_CoreMedia-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:057e63e533577fe5d764a5a9d307f60e8d9c58803112951ace42183abe9437e3", size = 29422, upload_time = "2025-01-14T18:50:56.964Z" }, - { url = "https://files.pythonhosted.org/packages/68/73/7ed3eba9c5a4a2071c3a64d6b1388d13474ad8d972529f3d5c950942513d/pyobjc_framework_CoreMedia-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:afd8eb59f5ce0730ff15476ad3989aa84ffb8d8d02c9b8b2c9c1248b0541dbff", size = 29297, upload_time = "2025-01-14T18:50:58.388Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b3/7baca352ddd7256840a4eb8f38fda39bc2e023b222b86d11c1a77cc0a8fa/pyobjc_framework_CoreMedia-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:057e63e533577fe5d764a5a9d307f60e8d9c58803112951ace42183abe9437e3", size = 29422, upload-time = "2025-01-14T18:50:56.964Z" }, + { url = "https://files.pythonhosted.org/packages/68/73/7ed3eba9c5a4a2071c3a64d6b1388d13474ad8d972529f3d5c950942513d/pyobjc_framework_CoreMedia-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:afd8eb59f5ce0730ff15476ad3989aa84ffb8d8d02c9b8b2c9c1248b0541dbff", size = 29297, upload-time = "2025-01-14T18:50:58.388Z" }, ] [[package]] @@ -2589,10 +2589,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856, upload_time = "2025-01-14T19:03:14.225Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856, upload-time = "2025-01-14T19:03:14.225Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/02/09fda96c4727ff0c632c3cf4b09faa5b82be9f18422860dd80b5bc676ae1/pyobjc_framework_CoreMediaIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4182b91c72923d5c4d52eca3c221cc6f149d80a96242c0caab1d5bc9ccbcbbb", size = 17492, upload_time = "2025-01-14T18:51:04.559Z" }, - { url = "https://files.pythonhosted.org/packages/3f/db/a7b11cbf7d31964a65c5593ac30a02b0db35260845431046d467b08fc059/pyobjc_framework_CoreMediaIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ad1e0f74126b6c6d25017e4ba08f66fe5422c902060d64b69e06a0c10214355", size = 17534, upload_time = "2025-01-14T18:51:06.752Z" }, + { url = "https://files.pythonhosted.org/packages/27/02/09fda96c4727ff0c632c3cf4b09faa5b82be9f18422860dd80b5bc676ae1/pyobjc_framework_CoreMediaIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4182b91c72923d5c4d52eca3c221cc6f149d80a96242c0caab1d5bc9ccbcbbb", size = 17492, upload-time = "2025-01-14T18:51:04.559Z" }, + { url = "https://files.pythonhosted.org/packages/3f/db/a7b11cbf7d31964a65c5593ac30a02b0db35260845431046d467b08fc059/pyobjc_framework_CoreMediaIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ad1e0f74126b6c6d25017e4ba08f66fe5422c902060d64b69e06a0c10214355", size = 17534, upload-time = "2025-01-14T18:51:06.752Z" }, ] [[package]] @@ -2603,10 +2603,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342, upload_time = "2025-01-14T19:03:15.235Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342, upload-time = "2025-01-14T19:03:15.235Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/b9/c67886891ad3cd21107cf1e65f1431cbdcff33acd74bf55ad3d6e10f3adf/pyobjc_framework_CoreMIDI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:78dec1bcd253a0385ac0b758a455e2a9367fc3cb9e2306d410c61bafa8d4c2eb", size = 24314, upload_time = "2025-01-14T18:50:44.817Z" }, - { url = "https://files.pythonhosted.org/packages/c0/7a/0639bc1ac35373b68f0f15fbcb9bb4f317cc4452997ea8e611ce79f623e9/pyobjc_framework_CoreMIDI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:97158830d76b999255d87191f31624d4373ee8ff662af4f4376c584cfb805573", size = 24346, upload_time = "2025-01-14T18:50:45.805Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b9/c67886891ad3cd21107cf1e65f1431cbdcff33acd74bf55ad3d6e10f3adf/pyobjc_framework_CoreMIDI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:78dec1bcd253a0385ac0b758a455e2a9367fc3cb9e2306d410c61bafa8d4c2eb", size = 24314, upload-time = "2025-01-14T18:50:44.817Z" }, + { url = "https://files.pythonhosted.org/packages/c0/7a/0639bc1ac35373b68f0f15fbcb9bb4f317cc4452997ea8e611ce79f623e9/pyobjc_framework_CoreMIDI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:97158830d76b999255d87191f31624d4373ee8ff662af4f4376c584cfb805573", size = 24346, upload-time = "2025-01-14T18:50:45.805Z" }, ] [[package]] @@ -2617,10 +2617,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452, upload_time = "2025-01-14T19:03:16.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452, upload-time = "2025-01-14T19:03:16.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/dc/334823bb3faa490259df7611772e804eb883c56436fc69123e8a3a5ba0ea/pyobjc_framework_CoreML-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e290ad9c0ac5f057ce3885d35e33fadc115f59111f2e04f168c45e2890cb86e8", size = 11320, upload_time = "2025-01-14T18:50:50.914Z" }, - { url = "https://files.pythonhosted.org/packages/90/9f/3d053b95fbeeaf480d33fcc067504e205049591f6bee17e3a700b988d96c/pyobjc_framework_CoreML-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:48320a57589634c206d659799284a5133aaa006cf4562f772697df5b479043e4", size = 11321, upload_time = "2025-01-14T18:50:51.803Z" }, + { url = "https://files.pythonhosted.org/packages/50/dc/334823bb3faa490259df7611772e804eb883c56436fc69123e8a3a5ba0ea/pyobjc_framework_CoreML-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e290ad9c0ac5f057ce3885d35e33fadc115f59111f2e04f168c45e2890cb86e8", size = 11320, upload-time = "2025-01-14T18:50:50.914Z" }, + { url = "https://files.pythonhosted.org/packages/90/9f/3d053b95fbeeaf480d33fcc067504e205049591f6bee17e3a700b988d96c/pyobjc_framework_CoreML-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:48320a57589634c206d659799284a5133aaa006cf4562f772697df5b479043e4", size = 11321, upload-time = "2025-01-14T18:50:51.803Z" }, ] [[package]] @@ -2631,10 +2631,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508, upload_time = "2025-01-14T19:03:17.254Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508, upload-time = "2025-01-14T19:03:17.254Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/35/da29fd7350cd68bfe70f30a9e03e1350d7363c7c4fcdb5b0cd16f4bb47e2/pyobjc_framework_CoreMotion-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8e32de44e30028500e4d17c114eea69e7e74e5ae7aef6c208edc5bac34dfc21e", size = 10229, upload_time = "2025-01-14T18:51:12.825Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f6/8061b58f0f3e1daf34c19511f0eeefe4ad66d10d1994b84d7fa3733b7852/pyobjc_framework_CoreMotion-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:697a3121615e95e56808f388b0882217a50e5ff6b459eccae93f2809d5ea4389", size = 10250, upload_time = "2025-01-14T18:51:13.746Z" }, + { url = "https://files.pythonhosted.org/packages/01/35/da29fd7350cd68bfe70f30a9e03e1350d7363c7c4fcdb5b0cd16f4bb47e2/pyobjc_framework_CoreMotion-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8e32de44e30028500e4d17c114eea69e7e74e5ae7aef6c208edc5bac34dfc21e", size = 10229, upload-time = "2025-01-14T18:51:12.825Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f6/8061b58f0f3e1daf34c19511f0eeefe4ad66d10d1994b84d7fa3733b7852/pyobjc_framework_CoreMotion-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:697a3121615e95e56808f388b0882217a50e5ff6b459eccae93f2809d5ea4389", size = 10250, upload-time = "2025-01-14T18:51:13.746Z" }, ] [[package]] @@ -2646,10 +2646,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-fsevents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406, upload_time = "2025-01-14T19:03:19.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406, upload-time = "2025-01-14T19:03:19.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/cc/3899a59ed62fa36d2c1b95b94ff52e181ac48fde4011b68ca6abcbddd47a/pyobjc_framework_CoreServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7538ca6e22f4da0c3a70ddd9781f9240a3fe2fd7a7aa4dfb31c31f2532f008e", size = 30258, upload_time = "2025-01-14T18:51:20.487Z" }, - { url = "https://files.pythonhosted.org/packages/82/7b/8e059764951d0414f053bfebb6b1fba803a3b14397755cfd388b0a6363a7/pyobjc_framework_CoreServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b175b5aa7a78484fd07b93533174b125901a6b791df2c51e05df1ea5d5badab", size = 30250, upload_time = "2025-01-14T18:51:21.448Z" }, + { url = "https://files.pythonhosted.org/packages/e9/cc/3899a59ed62fa36d2c1b95b94ff52e181ac48fde4011b68ca6abcbddd47a/pyobjc_framework_CoreServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7538ca6e22f4da0c3a70ddd9781f9240a3fe2fd7a7aa4dfb31c31f2532f008e", size = 30258, upload-time = "2025-01-14T18:51:20.487Z" }, + { url = "https://files.pythonhosted.org/packages/82/7b/8e059764951d0414f053bfebb6b1fba803a3b14397755cfd388b0a6363a7/pyobjc_framework_CoreServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3b175b5aa7a78484fd07b93533174b125901a6b791df2c51e05df1ea5d5badab", size = 30250, upload-time = "2025-01-14T18:51:21.448Z" }, ] [[package]] @@ -2660,10 +2660,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130, upload_time = "2025-01-14T19:03:20.457Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130, upload-time = "2025-01-14T19:03:20.457Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/f1/54f9522d7f6ec7a6618c86abe0236869f61dd371b49df02dff7930433656/pyobjc_framework_CoreSpotlight-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:551878bfb9cc815fe2532fdf455f500dda44f8cd203dd836a6f1eb5cc0d49a9a", size = 9579, upload_time = "2025-01-14T18:51:28.889Z" }, - { url = "https://files.pythonhosted.org/packages/6c/24/dae8d0be7cb90328a8c1100c454e52faef95acc59940796f530b665b9555/pyobjc_framework_CoreSpotlight-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b0c595d0a422a0f81008df93a0a2b38a1fd62434c6f61e31f1dceec927803b80", size = 9597, upload_time = "2025-01-14T18:51:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f1/54f9522d7f6ec7a6618c86abe0236869f61dd371b49df02dff7930433656/pyobjc_framework_CoreSpotlight-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:551878bfb9cc815fe2532fdf455f500dda44f8cd203dd836a6f1eb5cc0d49a9a", size = 9579, upload-time = "2025-01-14T18:51:28.889Z" }, + { url = "https://files.pythonhosted.org/packages/6c/24/dae8d0be7cb90328a8c1100c454e52faef95acc59940796f530b665b9555/pyobjc_framework_CoreSpotlight-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b0c595d0a422a0f81008df93a0a2b38a1fd62434c6f61e31f1dceec927803b80", size = 9597, upload-time = "2025-01-14T18:51:30.677Z" }, ] [[package]] @@ -2675,10 +2675,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222, upload_time = "2025-01-14T19:03:21.521Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222, upload-time = "2025-01-14T19:03:21.521Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/20/b8a967101b585a2425ffe645135f8618edd51e1430aeb668373475a07d1f/pyobjc_framework_CoreText-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56a4889858308b0d9f147d568b4d91c441cc0ffd332497cb4f709bb1990450c1", size = 30397, upload_time = "2025-01-14T18:51:35.844Z" }, - { url = "https://files.pythonhosted.org/packages/0d/14/d300b8bf18acd1d98d40820d2a9b5c5b6cf96325bdfc5020bc963218e001/pyobjc_framework_CoreText-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb90e7f370b3fd7cb2fb442e3dc63fedf0b4af6908db1c18df694d10dc94669d", size = 30456, upload_time = "2025-01-14T18:51:36.962Z" }, + { url = "https://files.pythonhosted.org/packages/f6/20/b8a967101b585a2425ffe645135f8618edd51e1430aeb668373475a07d1f/pyobjc_framework_CoreText-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56a4889858308b0d9f147d568b4d91c441cc0ffd332497cb4f709bb1990450c1", size = 30397, upload-time = "2025-01-14T18:51:35.844Z" }, + { url = "https://files.pythonhosted.org/packages/0d/14/d300b8bf18acd1d98d40820d2a9b5c5b6cf96325bdfc5020bc963218e001/pyobjc_framework_CoreText-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb90e7f370b3fd7cb2fb442e3dc63fedf0b4af6908db1c18df694d10dc94669d", size = 30456, upload-time = "2025-01-14T18:51:36.962Z" }, ] [[package]] @@ -2689,10 +2689,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254, upload_time = "2025-01-14T19:03:23.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254, upload-time = "2025-01-14T19:03:23.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/e7/a869bf3e8673c8fdf496706672dac77fc305493db3c1057e3ca5f8d49c3f/pyobjc_framework_CoreWLAN-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4384ba68d4beb4d610ca0d661593e16efe541faf1790222b898b3f4dd389c98a", size = 9895, upload_time = "2025-01-14T18:51:43.245Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d7/87626e23f010aa865eef10c796d1d87ddd87b78656f4e4ef0e808c8268f7/pyobjc_framework_CoreWLAN-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f5c365f6ebdae4a87d534cf8af877a57d2aabe50fe5949a9334e75173291898", size = 9917, upload_time = "2025-01-14T18:51:45.362Z" }, + { url = "https://files.pythonhosted.org/packages/da/e7/a869bf3e8673c8fdf496706672dac77fc305493db3c1057e3ca5f8d49c3f/pyobjc_framework_CoreWLAN-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4384ba68d4beb4d610ca0d661593e16efe541faf1790222b898b3f4dd389c98a", size = 9895, upload-time = "2025-01-14T18:51:43.245Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d7/87626e23f010aa865eef10c796d1d87ddd87b78656f4e4ef0e808c8268f7/pyobjc_framework_CoreWLAN-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5f5c365f6ebdae4a87d534cf8af877a57d2aabe50fe5949a9334e75173291898", size = 9917, upload-time = "2025-01-14T18:51:45.362Z" }, ] [[package]] @@ -2703,10 +2703,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734, upload_time = "2025-01-14T19:03:24.851Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734, upload-time = "2025-01-14T19:03:24.851Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/60/ddf022ce94f829a605992f11b9bfa861d7a1579f794e03d969c209d0de2a/pyobjc_framework_CryptoTokenKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c42620047cc75a749fbed045d181dc76284bc27edea904b97df1ad82c2fdafc", size = 12949, upload_time = "2025-01-14T18:51:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/d7/2d/9641cae1800281faace48698646f71c3de23ea1343031c12f6637d31e6f1/pyobjc_framework_CryptoTokenKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:95b05efb06b09987e23fb62dc3af378f38cfd0bd5872940cd95cf0f39dac6a57", size = 12978, upload_time = "2025-01-14T18:51:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/ac/60/ddf022ce94f829a605992f11b9bfa861d7a1579f794e03d969c209d0de2a/pyobjc_framework_CryptoTokenKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c42620047cc75a749fbed045d181dc76284bc27edea904b97df1ad82c2fdafc", size = 12949, upload-time = "2025-01-14T18:51:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2d/9641cae1800281faace48698646f71c3de23ea1343031c12f6637d31e6f1/pyobjc_framework_CryptoTokenKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:95b05efb06b09987e23fb62dc3af378f38cfd0bd5872940cd95cf0f39dac6a57", size = 12978, upload-time = "2025-01-14T18:51:51.215Z" }, ] [[package]] @@ -2717,10 +2717,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738, upload_time = "2025-01-14T19:03:27.054Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738, upload-time = "2025-01-14T19:03:27.054Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/a1/63653827a78c8329a0106ac06e68ec0434e7f104f022dee5929bdf8fed62/pyobjc_framework_DataDetection-11.0-py2.py3-none-any.whl", hash = "sha256:0fd191ddee9bc6a491e05dfb7de780c0266fd6c90ca783e168786c4b0b5d7d7c", size = 3428, upload_time = "2025-01-14T18:51:58.111Z" }, - { url = "https://files.pythonhosted.org/packages/1b/61/ee4579efb7c02b794d26ab0458722598726678d0bb227c9aa925a34f36af/pyobjc_framework_DataDetection-11.0-py3-none-any.whl", hash = "sha256:21b4a1dbf6cb56fdc971224476453dd1a7a4bb72d2c670444e81ae96fde97cb2", size = 3501, upload_time = "2025-01-14T18:51:59.104Z" }, + { url = "https://files.pythonhosted.org/packages/11/a1/63653827a78c8329a0106ac06e68ec0434e7f104f022dee5929bdf8fed62/pyobjc_framework_DataDetection-11.0-py2.py3-none-any.whl", hash = "sha256:0fd191ddee9bc6a491e05dfb7de780c0266fd6c90ca783e168786c4b0b5d7d7c", size = 3428, upload-time = "2025-01-14T18:51:58.111Z" }, + { url = "https://files.pythonhosted.org/packages/1b/61/ee4579efb7c02b794d26ab0458722598726678d0bb227c9aa925a34f36af/pyobjc_framework_DataDetection-11.0-py3-none-any.whl", hash = "sha256:21b4a1dbf6cb56fdc971224476453dd1a7a4bb72d2c670444e81ae96fde97cb2", size = 3501, upload-time = "2025-01-14T18:51:59.104Z" }, ] [[package]] @@ -2731,10 +2731,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198, upload_time = "2025-01-14T19:03:27.918Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198, upload-time = "2025-01-14T19:03:27.918Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/c1/d889e1c515c23b911594aa0b53a9d8ab6173e07adaaad8db89324a731fb7/pyobjc_framework_DeviceCheck-11.0-py2.py3-none-any.whl", hash = "sha256:d9252173a57dfba09ae37ccc3049f4b4990c1cbdcde338622b42c66296a8740e", size = 3612, upload_time = "2025-01-14T18:52:00.097Z" }, - { url = "https://files.pythonhosted.org/packages/65/8b/fa0cc2da2d49897f64e27a8a4e2a68f5784515f1adcea3a90f90b8ae8d44/pyobjc_framework_DeviceCheck-11.0-py3-none-any.whl", hash = "sha256:e8ed3965808963b2f0a7e069537d752bc659b75db1901cc24e5138925b9a7052", size = 3684, upload_time = "2025-01-14T18:52:02.389Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c1/d889e1c515c23b911594aa0b53a9d8ab6173e07adaaad8db89324a731fb7/pyobjc_framework_DeviceCheck-11.0-py2.py3-none-any.whl", hash = "sha256:d9252173a57dfba09ae37ccc3049f4b4990c1cbdcde338622b42c66296a8740e", size = 3612, upload-time = "2025-01-14T18:52:00.097Z" }, + { url = "https://files.pythonhosted.org/packages/65/8b/fa0cc2da2d49897f64e27a8a4e2a68f5784515f1adcea3a90f90b8ae8d44/pyobjc_framework_DeviceCheck-11.0-py3-none-any.whl", hash = "sha256:e8ed3965808963b2f0a7e069537d752bc659b75db1901cc24e5138925b9a7052", size = 3684, upload-time = "2025-01-14T18:52:02.389Z" }, ] [[package]] @@ -2745,10 +2745,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511, upload_time = "2025-01-14T19:03:32.132Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511, upload-time = "2025-01-14T19:03:32.132Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/be/3353a87691796a277ff4c048c4fa9a43db6f353fd683e8bb9e297651950c/pyobjc_framework_DeviceDiscoveryExtension-11.0-py2.py3-none-any.whl", hash = "sha256:82032e567d0031839d626947368d6d3d4ca97c915f15d2779a444cf4b2ffa4a3", size = 4194, upload_time = "2025-01-14T18:52:03.253Z" }, - { url = "https://files.pythonhosted.org/packages/06/87/52137a60498c03ab0acd3b9eadafe3c371c12e0549718e6a1f0fff8b7725/pyobjc_framework_DeviceDiscoveryExtension-11.0-py3-none-any.whl", hash = "sha256:9c94057173f13472089d561b780d93b5aa244d048b4760a0e1ab54fe7c2253c5", size = 4265, upload_time = "2025-01-14T18:52:05.101Z" }, + { url = "https://files.pythonhosted.org/packages/2a/be/3353a87691796a277ff4c048c4fa9a43db6f353fd683e8bb9e297651950c/pyobjc_framework_DeviceDiscoveryExtension-11.0-py2.py3-none-any.whl", hash = "sha256:82032e567d0031839d626947368d6d3d4ca97c915f15d2779a444cf4b2ffa4a3", size = 4194, upload-time = "2025-01-14T18:52:03.253Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/52137a60498c03ab0acd3b9eadafe3c371c12e0549718e6a1f0fff8b7725/pyobjc_framework_DeviceDiscoveryExtension-11.0-py3-none-any.whl", hash = "sha256:9c94057173f13472089d561b780d93b5aa244d048b4760a0e1ab54fe7c2253c5", size = 4265, upload-time = "2025-01-14T18:52:05.101Z" }, ] [[package]] @@ -2759,10 +2759,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-coreservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823, upload_time = "2025-01-14T19:03:32.942Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823, upload-time = "2025-01-14T19:03:32.942Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/68/5ea9766a8a6301f1a2ee39d595fe03d50b84b979d3d059e3e0ff541eab45/pyobjc_framework_DictionaryServices-11.0-py2.py3-none-any.whl", hash = "sha256:7c081371855240ac8e22783a71f32393c0f1e0b94d2fd193e8fef0a8be007080", size = 3829, upload_time = "2025-01-14T18:52:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c4/62b73f813c012f72a3a8e2f6326506803b45e91dc4ce6683e02a52a7f414/pyobjc_framework_DictionaryServices-11.0-py3-none-any.whl", hash = "sha256:15cdc3b64cb73713ee928cdcc0a12c845729f117bb8e73c7511f6e3f256d9d39", size = 3901, upload_time = "2025-01-14T18:52:08.403Z" }, + { url = "https://files.pythonhosted.org/packages/0a/68/5ea9766a8a6301f1a2ee39d595fe03d50b84b979d3d059e3e0ff541eab45/pyobjc_framework_DictionaryServices-11.0-py2.py3-none-any.whl", hash = "sha256:7c081371855240ac8e22783a71f32393c0f1e0b94d2fd193e8fef0a8be007080", size = 3829, upload-time = "2025-01-14T18:52:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c4/62b73f813c012f72a3a8e2f6326506803b45e91dc4ce6683e02a52a7f414/pyobjc_framework_DictionaryServices-11.0-py3-none-any.whl", hash = "sha256:15cdc3b64cb73713ee928cdcc0a12c845729f117bb8e73c7511f6e3f256d9d39", size = 3901, upload-time = "2025-01-14T18:52:08.403Z" }, ] [[package]] @@ -2773,10 +2773,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426, upload_time = "2025-01-14T19:03:35.589Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426, upload-time = "2025-01-14T19:03:35.589Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/0b/fbe460ccddb4c613eb04e2b81cc9c75b0e0c407fd9fb91776381416f99af/pyobjc_framework_DiscRecording-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eab79d83c2d974aa5564f3f6f4415218573dca69010026d2d000d232494a9d81", size = 14491, upload_time = "2025-01-14T18:52:10.415Z" }, - { url = "https://files.pythonhosted.org/packages/10/6f/c4c220d979771f4d7782deddef5ea9026baa177abe81cbe63d626a215de7/pyobjc_framework_DiscRecording-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e309e7394aed23d6ccce2e035f23c0c015d029c2ad531c6b1dce820b7eea8512", size = 14505, upload_time = "2025-01-14T18:52:11.414Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0b/fbe460ccddb4c613eb04e2b81cc9c75b0e0c407fd9fb91776381416f99af/pyobjc_framework_DiscRecording-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eab79d83c2d974aa5564f3f6f4415218573dca69010026d2d000d232494a9d81", size = 14491, upload-time = "2025-01-14T18:52:10.415Z" }, + { url = "https://files.pythonhosted.org/packages/10/6f/c4c220d979771f4d7782deddef5ea9026baa177abe81cbe63d626a215de7/pyobjc_framework_DiscRecording-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e309e7394aed23d6ccce2e035f23c0c015d029c2ad531c6b1dce820b7eea8512", size = 14505, upload-time = "2025-01-14T18:52:11.414Z" }, ] [[package]] @@ -2788,10 +2788,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-discrecording" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614, upload_time = "2025-01-14T19:03:36.695Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614, upload-time = "2025-01-14T19:03:36.695Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/45/4852afc5e093b76ba8f718d80fe1cc8604122a752806354379a7dbc41dc3/pyobjc_framework_DiscRecordingUI-11.0-py2.py3-none-any.whl", hash = "sha256:1af226c9350bb1d49960c02505e1e2f286e9377040dc2777a3f9a318925e081b", size = 4671, upload_time = "2025-01-14T18:52:16.645Z" }, - { url = "https://files.pythonhosted.org/packages/98/01/c5645513eeaadf0b9e387849fa656fc22524a1881f0d3a44d5b78784f836/pyobjc_framework_DiscRecordingUI-11.0-py3-none-any.whl", hash = "sha256:943df030f497a5ab73e969a04df8a653138fb67ebcf2380fedb4b4886d4ffba0", size = 4736, upload_time = "2025-01-14T18:52:17.655Z" }, + { url = "https://files.pythonhosted.org/packages/de/45/4852afc5e093b76ba8f718d80fe1cc8604122a752806354379a7dbc41dc3/pyobjc_framework_DiscRecordingUI-11.0-py2.py3-none-any.whl", hash = "sha256:1af226c9350bb1d49960c02505e1e2f286e9377040dc2777a3f9a318925e081b", size = 4671, upload-time = "2025-01-14T18:52:16.645Z" }, + { url = "https://files.pythonhosted.org/packages/98/01/c5645513eeaadf0b9e387849fa656fc22524a1881f0d3a44d5b78784f836/pyobjc_framework_DiscRecordingUI-11.0-py3-none-any.whl", hash = "sha256:943df030f497a5ab73e969a04df8a653138fb67ebcf2380fedb4b4886d4ffba0", size = 4736, upload-time = "2025-01-14T18:52:17.655Z" }, ] [[package]] @@ -2802,10 +2802,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096, upload_time = "2025-01-14T19:03:37.659Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096, upload-time = "2025-01-14T19:03:37.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/f4/f7ad86b2bb922b94745c369b90420cda984e6ad1ac9eb79ec32f5e332123/pyobjc_framework_DiskArbitration-11.0-py2.py3-none-any.whl", hash = "sha256:58823297eb09ff020ee156649170ab824fec32825bd32f2814c32e005920a72c", size = 4793, upload_time = "2025-01-14T18:52:18.561Z" }, - { url = "https://files.pythonhosted.org/packages/8e/87/bf0fc2aa781a819421e572cf6315fae7d0baf46607f9a67c86525c7e0e03/pyobjc_framework_DiskArbitration-11.0-py3-none-any.whl", hash = "sha256:7d41189a2d82045a7195c4661d8ec16195b6325a2f68f9d960e9a9f6649d1131", size = 4865, upload_time = "2025-01-14T18:52:19.786Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f4/f7ad86b2bb922b94745c369b90420cda984e6ad1ac9eb79ec32f5e332123/pyobjc_framework_DiskArbitration-11.0-py2.py3-none-any.whl", hash = "sha256:58823297eb09ff020ee156649170ab824fec32825bd32f2814c32e005920a72c", size = 4793, upload-time = "2025-01-14T18:52:18.561Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/bf0fc2aa781a819421e572cf6315fae7d0baf46607f9a67c86525c7e0e03/pyobjc_framework_DiskArbitration-11.0-py3-none-any.whl", hash = "sha256:7d41189a2d82045a7195c4661d8ec16195b6325a2f68f9d960e9a9f6649d1131", size = 4865, upload-time = "2025-01-14T18:52:19.786Z" }, ] [[package]] @@ -2816,10 +2816,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798, upload_time = "2025-01-14T19:03:38.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798, upload-time = "2025-01-14T19:03:38.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/7f/6073ef2c5170abf55a15750cd069b0c3fdd03e48f3c86761a6a8ecaa0a38/pyobjc_framework_DVDPlayback-11.0-py2.py3-none-any.whl", hash = "sha256:2013289aa38166d81bcbf25d6600ead1996e50de2bc689e5cf36f36a45346424", size = 8171, upload_time = "2025-01-14T18:51:55.282Z" }, - { url = "https://files.pythonhosted.org/packages/db/e4/97ed8d41491f366908581efb8644376fd81ede07ec2cf204cdb3c300ed1e/pyobjc_framework_DVDPlayback-11.0-py3-none-any.whl", hash = "sha256:c6be6ae410d8dce7179d6ee8c9bc421468d4b9c19af3ff0e59c93ae71cfc33e0", size = 8245, upload_time = "2025-01-14T18:51:57.205Z" }, + { url = "https://files.pythonhosted.org/packages/6b/7f/6073ef2c5170abf55a15750cd069b0c3fdd03e48f3c86761a6a8ecaa0a38/pyobjc_framework_DVDPlayback-11.0-py2.py3-none-any.whl", hash = "sha256:2013289aa38166d81bcbf25d6600ead1996e50de2bc689e5cf36f36a45346424", size = 8171, upload-time = "2025-01-14T18:51:55.282Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/97ed8d41491f366908581efb8644376fd81ede07ec2cf204cdb3c300ed1e/pyobjc_framework_DVDPlayback-11.0-py3-none-any.whl", hash = "sha256:c6be6ae410d8dce7179d6ee8c9bc421468d4b9c19af3ff0e59c93ae71cfc33e0", size = 8245, upload-time = "2025-01-14T18:51:57.205Z" }, ] [[package]] @@ -2830,10 +2830,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399, upload_time = "2025-01-14T19:03:39.441Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399, upload-time = "2025-01-14T19:03:39.441Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/d5/e866c951237fb1b6423b85e1623a7f8cc417862261196e276ecc23141976/pyobjc_framework_EventKit-11.0-py2.py3-none-any.whl", hash = "sha256:934e31f4c82f887e1bf01f96d33de4c7c6727de3fdb55bc739e1c686c10cc151", size = 6717, upload_time = "2025-01-14T18:52:20.684Z" }, - { url = "https://files.pythonhosted.org/packages/dc/47/3c0cc7b8c95e6759804b426e78510f65b8e7409c425b85f1b0109d14cdcc/pyobjc_framework_EventKit-11.0-py3-none-any.whl", hash = "sha256:5467977c79649dac9e0183dc72511f7dd49aab0260b67c2cfa25079a5a303f11", size = 6789, upload_time = "2025-01-14T18:52:21.73Z" }, + { url = "https://files.pythonhosted.org/packages/97/d5/e866c951237fb1b6423b85e1623a7f8cc417862261196e276ecc23141976/pyobjc_framework_EventKit-11.0-py2.py3-none-any.whl", hash = "sha256:934e31f4c82f887e1bf01f96d33de4c7c6727de3fdb55bc739e1c686c10cc151", size = 6717, upload-time = "2025-01-14T18:52:20.684Z" }, + { url = "https://files.pythonhosted.org/packages/dc/47/3c0cc7b8c95e6759804b426e78510f65b8e7409c425b85f1b0109d14cdcc/pyobjc_framework_EventKit-11.0-py3-none-any.whl", hash = "sha256:5467977c79649dac9e0183dc72511f7dd49aab0260b67c2cfa25079a5a303f11", size = 6789, upload-time = "2025-01-14T18:52:21.73Z" }, ] [[package]] @@ -2844,10 +2844,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157, upload_time = "2025-01-14T19:03:40.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157, upload-time = "2025-01-14T19:03:40.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/9d/c25b0bc0d300dd5aedd61f0cbd94a91ec6608b550821108d554e9eea0ed7/pyobjc_framework_ExceptionHandling-11.0-py2.py3-none-any.whl", hash = "sha256:972e0a376fee4d3d4c5161f82a8e5f6305392dbf19e98c4c6486d737759ebd89", size = 6993, upload_time = "2025-01-14T18:52:22.621Z" }, - { url = "https://files.pythonhosted.org/packages/cb/04/4b75e083325313e80e66f42d9a932c3febd2db48609d5d960a319b568f7c/pyobjc_framework_ExceptionHandling-11.0-py3-none-any.whl", hash = "sha256:d7f95fdb60a2636416066d3d12fad06cbf597e038576f8ed46fd3c742cc22252", size = 7063, upload_time = "2025-01-14T18:52:24.447Z" }, + { url = "https://files.pythonhosted.org/packages/e7/9d/c25b0bc0d300dd5aedd61f0cbd94a91ec6608b550821108d554e9eea0ed7/pyobjc_framework_ExceptionHandling-11.0-py2.py3-none-any.whl", hash = "sha256:972e0a376fee4d3d4c5161f82a8e5f6305392dbf19e98c4c6486d737759ebd89", size = 6993, upload-time = "2025-01-14T18:52:22.621Z" }, + { url = "https://files.pythonhosted.org/packages/cb/04/4b75e083325313e80e66f42d9a932c3febd2db48609d5d960a319b568f7c/pyobjc_framework_ExceptionHandling-11.0-py3-none-any.whl", hash = "sha256:d7f95fdb60a2636416066d3d12fad06cbf597e038576f8ed46fd3c742cc22252", size = 7063, upload-time = "2025-01-14T18:52:24.447Z" }, ] [[package]] @@ -2858,10 +2858,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753, upload_time = "2025-01-14T19:03:42.353Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753, upload-time = "2025-01-14T19:03:42.353Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/03/a433c64c21c754ed796ae5ca0bad63fcb1d51134968ce0c53d4ee806ccd8/pyobjc_framework_ExecutionPolicy-11.0-py2.py3-none-any.whl", hash = "sha256:fdf78bf22fa6ea6f27b574f73856a8a22992d0c0d5a6ed64823e00000c06ffe7", size = 3668, upload_time = "2025-01-14T18:52:28.64Z" }, - { url = "https://files.pythonhosted.org/packages/0b/47/da969dd9d56403e23cc95e68c4816563f64ed6fde7ff4e3c3710e8e8efcf/pyobjc_framework_ExecutionPolicy-11.0-py3-none-any.whl", hash = "sha256:d2dba6f3f7803d1cd0a5608a7ad75085b73097b6c3a935b7f1326c7202249751", size = 3737, upload_time = "2025-01-14T18:52:30.841Z" }, + { url = "https://files.pythonhosted.org/packages/d5/03/a433c64c21c754ed796ae5ca0bad63fcb1d51134968ce0c53d4ee806ccd8/pyobjc_framework_ExecutionPolicy-11.0-py2.py3-none-any.whl", hash = "sha256:fdf78bf22fa6ea6f27b574f73856a8a22992d0c0d5a6ed64823e00000c06ffe7", size = 3668, upload-time = "2025-01-14T18:52:28.64Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/da969dd9d56403e23cc95e68c4816563f64ed6fde7ff4e3c3710e8e8efcf/pyobjc_framework_ExecutionPolicy-11.0-py3-none-any.whl", hash = "sha256:d2dba6f3f7803d1cd0a5608a7ad75085b73097b6c3a935b7f1326c7202249751", size = 3737, upload-time = "2025-01-14T18:52:30.841Z" }, ] [[package]] @@ -2872,10 +2872,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200, upload_time = "2025-01-14T19:03:43.188Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200, upload-time = "2025-01-14T19:03:43.188Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/1d/ed580ce024d7e9a1ea88ee592d03b34f0b688414793bf8b7be5a367ecea8/pyobjc_framework_ExtensionKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98957dd51f0a4e02aa3d9d3a184f37ca5f99f4cb9e11282a2fc793d18de02af8", size = 7781, upload_time = "2025-01-14T18:52:32.843Z" }, - { url = "https://files.pythonhosted.org/packages/fd/9e/a68989bf7bbba7b5fb1ade168d2179e37164439daaad63a27ccb790a6593/pyobjc_framework_ExtensionKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e341979ee4a7fc5978fe44d6d1d461c774411042cac4e119a32704d6c989de6f", size = 7783, upload_time = "2025-01-14T18:52:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/a7/1d/ed580ce024d7e9a1ea88ee592d03b34f0b688414793bf8b7be5a367ecea8/pyobjc_framework_ExtensionKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98957dd51f0a4e02aa3d9d3a184f37ca5f99f4cb9e11282a2fc793d18de02af8", size = 7781, upload-time = "2025-01-14T18:52:32.843Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9e/a68989bf7bbba7b5fb1ade168d2179e37164439daaad63a27ccb790a6593/pyobjc_framework_ExtensionKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e341979ee4a7fc5978fe44d6d1d461c774411042cac4e119a32704d6c989de6f", size = 7783, upload-time = "2025-01-14T18:52:33.711Z" }, ] [[package]] @@ -2886,10 +2886,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577, upload_time = "2025-01-14T19:03:44.021Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577, upload-time = "2025-01-14T19:03:44.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/96/bddfe9f72a59a3038ec3208a7d2a62332d5e171d7e3c338ccff6bd6e76b8/pyobjc_framework_ExternalAccessory-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:319f66edb96505f833fe7fe9ba810cb3b0d0c65605b8674bea52f040e8caebd6", size = 8785, upload_time = "2025-01-14T18:52:40.529Z" }, - { url = "https://files.pythonhosted.org/packages/e7/e2/26e9cbb18723200ef71580e46c46f037b7feecc07cf50051cd6fcb426472/pyobjc_framework_ExternalAccessory-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:aaae920c9241d1b35a58ba76dba761689b248250d782179526f6dea151b1fda0", size = 8808, upload_time = "2025-01-14T18:52:42.142Z" }, + { url = "https://files.pythonhosted.org/packages/a3/96/bddfe9f72a59a3038ec3208a7d2a62332d5e171d7e3c338ccff6bd6e76b8/pyobjc_framework_ExternalAccessory-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:319f66edb96505f833fe7fe9ba810cb3b0d0c65605b8674bea52f040e8caebd6", size = 8785, upload-time = "2025-01-14T18:52:40.529Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e2/26e9cbb18723200ef71580e46c46f037b7feecc07cf50051cd6fcb426472/pyobjc_framework_ExternalAccessory-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:aaae920c9241d1b35a58ba76dba761689b248250d782179526f6dea151b1fda0", size = 8808, upload-time = "2025-01-14T18:52:42.142Z" }, ] [[package]] @@ -2900,10 +2900,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701, upload_time = "2025-01-14T19:03:44.931Z" } +sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701, upload-time = "2025-01-14T19:03:44.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/57/1f959ec54650d1afc08e89d2995a1534f44229b1371cf66429a45b27c32d/pyobjc_framework_FileProvider-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c7e803a37f7327c191a4de7dbb36e5fbf8bd08dadbcc7f626e491451c7a3849", size = 19179, upload_time = "2025-01-14T18:53:01.659Z" }, - { url = "https://files.pythonhosted.org/packages/30/79/ff4dfe06eb43c97bd723f066ef2b92b00b1020206b4dcc5abe9b49746cad/pyobjc_framework_FileProvider-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d7acdc5e0f4b5488bcbf47d3eea469b22897a4b783fe3f5d4b2b1f3288e82038", size = 19154, upload_time = "2025-01-14T18:53:02.597Z" }, + { url = "https://files.pythonhosted.org/packages/4a/57/1f959ec54650d1afc08e89d2995a1534f44229b1371cf66429a45b27c32d/pyobjc_framework_FileProvider-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c7e803a37f7327c191a4de7dbb36e5fbf8bd08dadbcc7f626e491451c7a3849", size = 19179, upload-time = "2025-01-14T18:53:01.659Z" }, + { url = "https://files.pythonhosted.org/packages/30/79/ff4dfe06eb43c97bd723f066ef2b92b00b1020206b4dcc5abe9b49746cad/pyobjc_framework_FileProvider-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d7acdc5e0f4b5488bcbf47d3eea469b22897a4b783fe3f5d4b2b1f3288e82038", size = 19154, upload-time = "2025-01-14T18:53:02.597Z" }, ] [[package]] @@ -2914,10 +2914,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-fileprovider" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419, upload_time = "2025-01-14T19:03:46.016Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419, upload-time = "2025-01-14T19:03:46.016Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/2e/8a91cfa9485a2e9ad295da8bb5505d0dc1046dec8557d2ae17eef75f3912/pyobjc_framework_FileProviderUI-11.0-py2.py3-none-any.whl", hash = "sha256:5102651febb5a6140f99b116b73d0fd6c9822372a5203506e4904ac0ebb1313c", size = 3642, upload_time = "2025-01-14T18:53:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/75/9b/a542159b1aefedb24f01440a929b7bbc6f4bbae3a74d09ad05a7f4adb9c0/pyobjc_framework_FileProviderUI-11.0-py3-none-any.whl", hash = "sha256:b75f70eef2af3696f3cb2e0de88bbb437343b53070078573ae72d64bf56fce9d", size = 3712, upload_time = "2025-01-14T18:53:07.403Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/8a91cfa9485a2e9ad295da8bb5505d0dc1046dec8557d2ae17eef75f3912/pyobjc_framework_FileProviderUI-11.0-py2.py3-none-any.whl", hash = "sha256:5102651febb5a6140f99b116b73d0fd6c9822372a5203506e4904ac0ebb1313c", size = 3642, upload-time = "2025-01-14T18:53:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/75/9b/a542159b1aefedb24f01440a929b7bbc6f4bbae3a74d09ad05a7f4adb9c0/pyobjc_framework_FileProviderUI-11.0-py3-none-any.whl", hash = "sha256:b75f70eef2af3696f3cb2e0de88bbb437343b53070078573ae72d64bf56fce9d", size = 3712, upload-time = "2025-01-14T18:53:07.403Z" }, ] [[package]] @@ -2928,10 +2928,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295, upload_time = "2025-01-14T19:03:46.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295, upload-time = "2025-01-14T19:03:46.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/f1/42797ae9065e0127df4b5bb7a45e06eff8568a476edbc8d590cea9d25228/pyobjc_framework_FinderSync-11.0-py2.py3-none-any.whl", hash = "sha256:cafb262d1ad1e3a86af333f673aeda4f9bdcf528ded97c2232fd1cf440d1db5a", size = 4788, upload_time = "2025-01-14T18:53:09.559Z" }, - { url = "https://files.pythonhosted.org/packages/d8/96/2ed2ca5536f76102ea3bfb886cdc7b34ec51f53b122b9c535b4ac9b1ee03/pyobjc_framework_FinderSync-11.0-py3-none-any.whl", hash = "sha256:d00285b85038c5546e8566bec9cd3a4615708f0e6cb774d0ea804c69546ec915", size = 4860, upload_time = "2025-01-14T18:53:11.765Z" }, + { url = "https://files.pythonhosted.org/packages/96/f1/42797ae9065e0127df4b5bb7a45e06eff8568a476edbc8d590cea9d25228/pyobjc_framework_FinderSync-11.0-py2.py3-none-any.whl", hash = "sha256:cafb262d1ad1e3a86af333f673aeda4f9bdcf528ded97c2232fd1cf440d1db5a", size = 4788, upload-time = "2025-01-14T18:53:09.559Z" }, + { url = "https://files.pythonhosted.org/packages/d8/96/2ed2ca5536f76102ea3bfb886cdc7b34ec51f53b122b9c535b4ac9b1ee03/pyobjc_framework_FinderSync-11.0-py3-none-any.whl", hash = "sha256:d00285b85038c5546e8566bec9cd3a4615708f0e6cb774d0ea804c69546ec915", size = 4860, upload-time = "2025-01-14T18:53:11.765Z" }, ] [[package]] @@ -2942,10 +2942,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078, upload_time = "2025-01-14T19:03:49.762Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078, upload-time = "2025-01-14T19:03:49.762Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/8a/75fd630865c9f9d69b1364208582872fc818b4c1a70fd9ae85a5cf7a2c5a/pyobjc_framework_FSEvents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0b3c7835251a35453e3079cf929b9e5329d02e2f4eaac2ebabbe19e1abd18ab", size = 13209, upload_time = "2025-01-14T18:52:49.478Z" }, - { url = "https://files.pythonhosted.org/packages/19/c6/cae1a6a96ad493339e9f0f175bcf18c1526abe422b63309d873acd663dc2/pyobjc_framework_FSEvents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb8a5a7f7b5a70e15dae80672f10ecc16b5d1c1afe62ad2ccadb17a8098876cd", size = 13274, upload_time = "2025-01-14T18:52:51.588Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8a/75fd630865c9f9d69b1364208582872fc818b4c1a70fd9ae85a5cf7a2c5a/pyobjc_framework_FSEvents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0b3c7835251a35453e3079cf929b9e5329d02e2f4eaac2ebabbe19e1abd18ab", size = 13209, upload-time = "2025-01-14T18:52:49.478Z" }, + { url = "https://files.pythonhosted.org/packages/19/c6/cae1a6a96ad493339e9f0f175bcf18c1526abe422b63309d873acd663dc2/pyobjc_framework_FSEvents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb8a5a7f7b5a70e15dae80672f10ecc16b5d1c1afe62ad2ccadb17a8098876cd", size = 13274, upload-time = "2025-01-14T18:52:51.588Z" }, ] [[package]] @@ -2956,10 +2956,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459, upload_time = "2025-01-14T19:03:50.766Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459, upload-time = "2025-01-14T19:03:50.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/7e/8a41ab9880e415143baf771d55566e2a863ec538837480a5ee17e1ddc08b/pyobjc_framework_GameCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8bff2a36cf3cb52cbe321203147766e95997f881062143171cdd8ef2fde9e53", size = 18472, upload_time = "2025-01-14T18:53:13.794Z" }, - { url = "https://files.pythonhosted.org/packages/c4/78/846aa21be2303cba955aaf781a362504a722183b8f6a030ba02f2b2073ad/pyobjc_framework_GameCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de57380e3b51579a6e8bc397c2bb5be5d0f6dcd4bf5abed587700cf7f57afd4", size = 18437, upload_time = "2025-01-14T18:53:14.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/8a41ab9880e415143baf771d55566e2a863ec538837480a5ee17e1ddc08b/pyobjc_framework_GameCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8bff2a36cf3cb52cbe321203147766e95997f881062143171cdd8ef2fde9e53", size = 18472, upload-time = "2025-01-14T18:53:13.794Z" }, + { url = "https://files.pythonhosted.org/packages/c4/78/846aa21be2303cba955aaf781a362504a722183b8f6a030ba02f2b2073ad/pyobjc_framework_GameCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de57380e3b51579a6e8bc397c2bb5be5d0f6dcd4bf5abed587700cf7f57afd4", size = 18437, upload-time = "2025-01-14T18:53:14.802Z" }, ] [[package]] @@ -2970,10 +2970,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318, upload_time = "2025-01-14T19:03:52.264Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318, upload-time = "2025-01-14T19:03:52.264Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/ec/05f356ab2d747a385c2a68908f2f67ee1b1e7a169b1497b0771b2226a174/pyobjc_framework_GameController-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:30e8f251be49ff67491df758c73149e7c7e87bee89919966ed1b2bf56fdaacf7", size = 20995, upload_time = "2025-01-14T18:53:19.979Z" }, - { url = "https://files.pythonhosted.org/packages/66/b3/38319c9232e3508297bfedde700b125676845b1e27afe2bb681e8829f34a/pyobjc_framework_GameController-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:46403f23aaaf6a2e1a51e3954c53d6e910b80058117fdcf3a0a8100f25e30f07", size = 20919, upload_time = "2025-01-14T18:53:20.943Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/05f356ab2d747a385c2a68908f2f67ee1b1e7a169b1497b0771b2226a174/pyobjc_framework_GameController-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:30e8f251be49ff67491df758c73149e7c7e87bee89919966ed1b2bf56fdaacf7", size = 20995, upload-time = "2025-01-14T18:53:19.979Z" }, + { url = "https://files.pythonhosted.org/packages/66/b3/38319c9232e3508297bfedde700b125676845b1e27afe2bb681e8829f34a/pyobjc_framework_GameController-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:46403f23aaaf6a2e1a51e3954c53d6e910b80058117fdcf3a0a8100f25e30f07", size = 20919, upload-time = "2025-01-14T18:53:20.943Z" }, ] [[package]] @@ -2985,10 +2985,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394, upload_time = "2025-01-14T19:03:53.762Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394, upload-time = "2025-01-14T19:03:53.762Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/4d/9fe843671c7b94d8e8a925662775d4b2632c138c6a0a9d1bb2c379f225c0/pyobjc_framework_GameKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dabe856c8638940d2b346abc7a1828cca12d00b47d2951d0ac9f4e27ecc8d3ec", size = 21667, upload_time = "2025-01-14T18:53:27.239Z" }, - { url = "https://files.pythonhosted.org/packages/98/b2/d4d1f123fead83bf68eb4ecfab2125933f3114eaf2ed420d7bb99238ba67/pyobjc_framework_GameKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:40d506505f71ed57779c8be9b4e07ec9337c45aebe323b3f8dd8f8c75e6fce50", size = 21627, upload_time = "2025-01-14T18:53:28.228Z" }, + { url = "https://files.pythonhosted.org/packages/88/4d/9fe843671c7b94d8e8a925662775d4b2632c138c6a0a9d1bb2c379f225c0/pyobjc_framework_GameKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dabe856c8638940d2b346abc7a1828cca12d00b47d2951d0ac9f4e27ecc8d3ec", size = 21667, upload-time = "2025-01-14T18:53:27.239Z" }, + { url = "https://files.pythonhosted.org/packages/98/b2/d4d1f123fead83bf68eb4ecfab2125933f3114eaf2ed420d7bb99238ba67/pyobjc_framework_GameKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:40d506505f71ed57779c8be9b4e07ec9337c45aebe323b3f8dd8f8c75e6fce50", size = 21627, upload-time = "2025-01-14T18:53:28.228Z" }, ] [[package]] @@ -3000,10 +3000,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-spritekit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837, upload_time = "2025-01-14T19:03:56.127Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837, upload-time = "2025-01-14T19:03:56.127Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/e7/3530071bf1897f2fe2e5f0c54620f0df9fcac586b9ba6bb5726fc9d295c2/pyobjc_framework_GameplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:01f59bbf5beb0cfcfea17011987995f1cccf2ec081d91269f95e71283dd83c67", size = 13381, upload_time = "2025-01-14T18:53:34.217Z" }, - { url = "https://files.pythonhosted.org/packages/b9/07/075369dd9d4e3849646285d4083a9d28214fdd043b499c7929047b942c7f/pyobjc_framework_GameplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2d56af0a84439b3ddc64cdec90e3fab08b1d43da97bed0fb8d60714f47c4372", size = 13382, upload_time = "2025-01-14T18:53:36.303Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/3530071bf1897f2fe2e5f0c54620f0df9fcac586b9ba6bb5726fc9d295c2/pyobjc_framework_GameplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:01f59bbf5beb0cfcfea17011987995f1cccf2ec081d91269f95e71283dd83c67", size = 13381, upload-time = "2025-01-14T18:53:34.217Z" }, + { url = "https://files.pythonhosted.org/packages/b9/07/075369dd9d4e3849646285d4083a9d28214fdd043b499c7929047b942c7f/pyobjc_framework_GameplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f2d56af0a84439b3ddc64cdec90e3fab08b1d43da97bed0fb8d60714f47c4372", size = 13382, upload-time = "2025-01-14T18:53:36.303Z" }, ] [[package]] @@ -3014,10 +3014,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558, upload_time = "2025-01-14T19:03:57.117Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558, upload-time = "2025-01-14T19:03:57.117Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f0/66/36a2fa7ef61b54a8e283355518ed003aa28b26e1dfad9ecbb7f543a08acd/pyobjc_framework_HealthKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ee87453c28bd4040b12a3bc834f0ea1989e331400845a14079e8f4a6ede70aa2", size = 20139, upload_time = "2025-01-14T18:53:44.342Z" }, - { url = "https://files.pythonhosted.org/packages/5f/fd/95d40483d9d185317adbf8433d0c7e83ba36ec6c5a824159b87160f6cebe/pyobjc_framework_HealthKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:680da6d67b0c79d15e897f1c588a8b02d780573aef3692e982294c43727eecf3", size = 20163, upload_time = "2025-01-14T18:53:45.278Z" }, + { url = "https://files.pythonhosted.org/packages/f0/66/36a2fa7ef61b54a8e283355518ed003aa28b26e1dfad9ecbb7f543a08acd/pyobjc_framework_HealthKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ee87453c28bd4040b12a3bc834f0ea1989e331400845a14079e8f4a6ede70aa2", size = 20139, upload-time = "2025-01-14T18:53:44.342Z" }, + { url = "https://files.pythonhosted.org/packages/5f/fd/95d40483d9d185317adbf8433d0c7e83ba36ec6c5a824159b87160f6cebe/pyobjc_framework_HealthKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:680da6d67b0c79d15e897f1c588a8b02d780573aef3692e982294c43727eecf3", size = 20163, upload-time = "2025-01-14T18:53:45.278Z" }, ] [[package]] @@ -3028,10 +3028,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935, upload_time = "2025-01-14T19:03:58.548Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935, upload-time = "2025-01-14T19:03:58.548Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/fb/29f20521e0df5da0110f1d6a48e4ed3530a2c0b670bf62d89ceeddd42c18/pyobjc_framework_ImageCaptureCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3fd78aa4a69e24caed38ae17a69b973e505324d966df86b47441318800a52db9", size = 16611, upload_time = "2025-01-14T18:54:05.308Z" }, - { url = "https://files.pythonhosted.org/packages/0e/ce/404666e27318435a0513dcf64b85d7cd99195b2e822e03796b03af549c52/pyobjc_framework_ImageCaptureCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5cc6c6acbfca05977adc0e339e1225d5cd314af2fa455a70baebb54f9fb2b64", size = 16636, upload_time = "2025-01-14T18:54:06.288Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fb/29f20521e0df5da0110f1d6a48e4ed3530a2c0b670bf62d89ceeddd42c18/pyobjc_framework_ImageCaptureCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3fd78aa4a69e24caed38ae17a69b973e505324d966df86b47441318800a52db9", size = 16611, upload-time = "2025-01-14T18:54:05.308Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ce/404666e27318435a0513dcf64b85d7cd99195b2e822e03796b03af549c52/pyobjc_framework_ImageCaptureCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c5cc6c6acbfca05977adc0e339e1225d5cd314af2fa455a70baebb54f9fb2b64", size = 16636, upload-time = "2025-01-14T18:54:06.288Z" }, ] [[package]] @@ -3042,10 +3042,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684, upload_time = "2025-01-14T19:03:59.525Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684, upload-time = "2025-01-14T19:03:59.525Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/08/18572def66bf1e0ee6d079b45b34f8b4cbf2ab40b3024c351e4bd83cfa4c/pyobjc_framework_InputMethodKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a58273233e236cb9fa2d8d9295017c6bf26d6f47cc3a5dc9ba81f1c1e64a346", size = 9369, upload_time = "2025-01-14T18:54:12.452Z" }, - { url = "https://files.pythonhosted.org/packages/9d/c9/7793b0d7b363548e62499660899893dff2953ae3a56aa5080e9b199d1291/pyobjc_framework_InputMethodKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7869db2b08586e97181ec2b60b8f5b9d3a683097bae4ce4bb29dc3c5709c3f13", size = 9390, upload_time = "2025-01-14T18:54:13.383Z" }, + { url = "https://files.pythonhosted.org/packages/b5/08/18572def66bf1e0ee6d079b45b34f8b4cbf2ab40b3024c351e4bd83cfa4c/pyobjc_framework_InputMethodKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a58273233e236cb9fa2d8d9295017c6bf26d6f47cc3a5dc9ba81f1c1e64a346", size = 9369, upload-time = "2025-01-14T18:54:12.452Z" }, + { url = "https://files.pythonhosted.org/packages/9d/c9/7793b0d7b363548e62499660899893dff2953ae3a56aa5080e9b199d1291/pyobjc_framework_InputMethodKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7869db2b08586e97181ec2b60b8f5b9d3a683097bae4ce4bb29dc3c5709c3f13", size = 9390, upload-time = "2025-01-14T18:54:13.383Z" }, ] [[package]] @@ -3056,10 +3056,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687, upload_time = "2025-01-14T19:04:00.515Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687, upload-time = "2025-01-14T19:04:00.515Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/db/0f3334648a53c8ad663fd19d5421863cb0b711e38a2eb742798d50ed33ef/pyobjc_framework_InstallerPlugins-11.0-py2.py3-none-any.whl", hash = "sha256:cb21bfd5597233a2de3d8c0a8d50f23cf92c43e8963edf85787430ac3cadf4a3", size = 4716, upload_time = "2025-01-14T18:54:17.885Z" }, - { url = "https://files.pythonhosted.org/packages/f7/56/fe6f50d74d19b0f85035aba977db7039eedbd2de5ac991278a6a5be475a0/pyobjc_framework_InstallerPlugins-11.0-py3-none-any.whl", hash = "sha256:2221301f466d30d6fd32c7317560c85926a3ee93f1de52d320e3b3cd826a8f93", size = 4784, upload_time = "2025-01-14T18:54:19.028Z" }, + { url = "https://files.pythonhosted.org/packages/03/db/0f3334648a53c8ad663fd19d5421863cb0b711e38a2eb742798d50ed33ef/pyobjc_framework_InstallerPlugins-11.0-py2.py3-none-any.whl", hash = "sha256:cb21bfd5597233a2de3d8c0a8d50f23cf92c43e8963edf85787430ac3cadf4a3", size = 4716, upload-time = "2025-01-14T18:54:17.885Z" }, + { url = "https://files.pythonhosted.org/packages/f7/56/fe6f50d74d19b0f85035aba977db7039eedbd2de5ac991278a6a5be475a0/pyobjc_framework_InstallerPlugins-11.0-py3-none-any.whl", hash = "sha256:2221301f466d30d6fd32c7317560c85926a3ee93f1de52d320e3b3cd826a8f93", size = 4784, upload-time = "2025-01-14T18:54:19.028Z" }, ] [[package]] @@ -3071,10 +3071,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027, upload_time = "2025-01-14T19:04:01.405Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027, upload-time = "2025-01-14T19:04:01.405Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/41/4c0ec3d59f9930e9c52570f7e26d79055881e0009e07466b4988c107ef7c/pyobjc_framework_InstantMessage-11.0-py2.py3-none-any.whl", hash = "sha256:ce364e4e18ec8551512b7d968c0d950ccf7de4bb470f66fe524f3bc8d23df0d1", size = 5334, upload_time = "2025-01-14T18:54:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/19/d9/e3620a5316c986b27361d2f21dd74b48f70c6f7bfe580075e970ca9d7bd6/pyobjc_framework_InstantMessage-11.0-py3-none-any.whl", hash = "sha256:a2817353eaf8f37fe6063c28006b2a0889892e3de801b51b059c153a9d3f35f8", size = 5402, upload_time = "2025-01-14T18:54:21.261Z" }, + { url = "https://files.pythonhosted.org/packages/8c/41/4c0ec3d59f9930e9c52570f7e26d79055881e0009e07466b4988c107ef7c/pyobjc_framework_InstantMessage-11.0-py2.py3-none-any.whl", hash = "sha256:ce364e4e18ec8551512b7d968c0d950ccf7de4bb470f66fe524f3bc8d23df0d1", size = 5334, upload-time = "2025-01-14T18:54:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/19/d9/e3620a5316c986b27361d2f21dd74b48f70c6f7bfe580075e970ca9d7bd6/pyobjc_framework_InstantMessage-11.0-py3-none-any.whl", hash = "sha256:a2817353eaf8f37fe6063c28006b2a0889892e3de801b51b059c153a9d3f35f8", size = 5402, upload-time = "2025-01-14T18:54:21.261Z" }, ] [[package]] @@ -3085,10 +3085,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921, upload_time = "2025-01-14T19:04:02.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921, upload-time = "2025-01-14T19:04:02.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/2e/cd8a4aa10a1d3808dd6f71195a28906c573345673dd92b774fbb8c93dd75/pyobjc_framework_Intents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9a445a3d4a0622ebf96c65b0ac0be7cec1e72cf7fd9900cd5ace6acf4e84bce", size = 32021, upload_time = "2025-01-14T18:54:23.325Z" }, - { url = "https://files.pythonhosted.org/packages/4a/0e/05c457dab601e3eb5ed7243a04fede32423f08dd03a08e988611359d55b4/pyobjc_framework_Intents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1e148accce2c7c9243ff90ab3f1a200f93d93506da9c3a2cd034fd5579cb839a", size = 32008, upload_time = "2025-01-14T18:54:24.317Z" }, + { url = "https://files.pythonhosted.org/packages/86/2e/cd8a4aa10a1d3808dd6f71195a28906c573345673dd92b774fbb8c93dd75/pyobjc_framework_Intents-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a9a445a3d4a0622ebf96c65b0ac0be7cec1e72cf7fd9900cd5ace6acf4e84bce", size = 32021, upload-time = "2025-01-14T18:54:23.325Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0e/05c457dab601e3eb5ed7243a04fede32423f08dd03a08e988611359d55b4/pyobjc_framework_Intents-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1e148accce2c7c9243ff90ab3f1a200f93d93506da9c3a2cd034fd5579cb839a", size = 32008, upload-time = "2025-01-14T18:54:24.317Z" }, ] [[package]] @@ -3099,10 +3099,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-intents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774, upload_time = "2025-01-14T19:04:03.648Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774, upload-time = "2025-01-14T19:04:03.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/19/f32a14585e749258bb945805da93fd71e05534b14e09fab243fb5ec507ff/pyobjc_framework_IntentsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c677225d38fffc5e00df803f93a6a627c466b35a362ed27173f7901e185882e", size = 8772, upload_time = "2025-01-14T18:54:29.958Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d4/e81e9cfafef63cef481ab251a961ca98e176ca244be91368e0f6b6fe8793/pyobjc_framework_IntentsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b93a1d9594f471596f255db354c13d67caed7aa020afb9f4e69cde2674f4db71", size = 8789, upload_time = "2025-01-14T18:54:30.984Z" }, + { url = "https://files.pythonhosted.org/packages/89/19/f32a14585e749258bb945805da93fd71e05534b14e09fab243fb5ec507ff/pyobjc_framework_IntentsUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c677225d38fffc5e00df803f93a6a627c466b35a362ed27173f7901e185882e", size = 8772, upload-time = "2025-01-14T18:54:29.958Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d4/e81e9cfafef63cef481ab251a961ca98e176ca244be91368e0f6b6fe8793/pyobjc_framework_IntentsUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b93a1d9594f471596f255db354c13d67caed7aa020afb9f4e69cde2674f4db71", size = 8789, upload-time = "2025-01-14T18:54:30.984Z" }, ] [[package]] @@ -3113,10 +3113,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463, upload_time = "2025-01-14T19:04:04.582Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463, upload-time = "2025-01-14T19:04:04.582Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/2e/2037b1c3459008ccdc41d65ab236d7919eed9bbadd0f02f65dc0193bb170/pyobjc_framework_IOBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7d2005d3eff2afed4b5930613ae3c1b50004ebabffb86c0d5dd28d54436e16e6", size = 41010, upload_time = "2025-01-14T18:53:51.62Z" }, - { url = "https://files.pythonhosted.org/packages/2a/52/c266636ff3edc98c1aaf2cc154392876a68d4167bed0351dc2933d5ccc3c/pyobjc_framework_IOBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f86d2e675ee2a61ba3d2a446322e918e8ef2dc3e242e893ef81abfc480a6f2c2", size = 41012, upload_time = "2025-01-14T18:53:52.757Z" }, + { url = "https://files.pythonhosted.org/packages/7a/2e/2037b1c3459008ccdc41d65ab236d7919eed9bbadd0f02f65dc0193bb170/pyobjc_framework_IOBluetooth-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7d2005d3eff2afed4b5930613ae3c1b50004ebabffb86c0d5dd28d54436e16e6", size = 41010, upload-time = "2025-01-14T18:53:51.62Z" }, + { url = "https://files.pythonhosted.org/packages/2a/52/c266636ff3edc98c1aaf2cc154392876a68d4167bed0351dc2933d5ccc3c/pyobjc_framework_IOBluetooth-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f86d2e675ee2a61ba3d2a446322e918e8ef2dc3e242e893ef81abfc480a6f2c2", size = 41012, upload-time = "2025-01-14T18:53:52.757Z" }, ] [[package]] @@ -3127,10 +3127,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-iobluetooth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091, upload_time = "2025-01-14T19:04:05.659Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091, upload-time = "2025-01-14T19:04:05.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/75/9401ae099f32a6be2e5759f8d25c573bcf103833343457ca5981153262ab/pyobjc_framework_IOBluetoothUI-11.0-py2.py3-none-any.whl", hash = "sha256:0f94afeb5ecbde07712ea7658a38d6b0e3558154a6bc29c9a33b633f5952b2c3", size = 3972, upload_time = "2025-01-14T18:53:57.925Z" }, - { url = "https://files.pythonhosted.org/packages/11/a3/75e473de9d25084bfbfa4c0ba24edf038956a604d78219894dc0b412e501/pyobjc_framework_IOBluetoothUI-11.0-py3-none-any.whl", hash = "sha256:5bc366a9904532168ac2c49523e7f090f81b6acbb7b8929ffc7855be0b1d4cf7", size = 4043, upload_time = "2025-01-14T18:54:00.086Z" }, + { url = "https://files.pythonhosted.org/packages/d9/75/9401ae099f32a6be2e5759f8d25c573bcf103833343457ca5981153262ab/pyobjc_framework_IOBluetoothUI-11.0-py2.py3-none-any.whl", hash = "sha256:0f94afeb5ecbde07712ea7658a38d6b0e3558154a6bc29c9a33b633f5952b2c3", size = 3972, upload-time = "2025-01-14T18:53:57.925Z" }, + { url = "https://files.pythonhosted.org/packages/11/a3/75e473de9d25084bfbfa4c0ba24edf038956a604d78219894dc0b412e501/pyobjc_framework_IOBluetoothUI-11.0-py3-none-any.whl", hash = "sha256:5bc366a9904532168ac2c49523e7f090f81b6acbb7b8929ffc7855be0b1d4cf7", size = 4043, upload-time = "2025-01-14T18:54:00.086Z" }, ] [[package]] @@ -3141,10 +3141,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555, upload_time = "2025-01-14T19:04:09.475Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555, upload-time = "2025-01-14T19:04:09.475Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/08/b96f84b623e2dd2ef733ccdd67a1694f51bfdb4dfd81d38e7755566ab9e5/pyobjc_framework_IOSurface-11.0-py2.py3-none-any.whl", hash = "sha256:58c6e79401a00dc63a5797cd3cc067542d4f94fcd2fc8979dc248c3b06c3b829", size = 4905, upload_time = "2025-01-14T18:54:00.978Z" }, - { url = "https://files.pythonhosted.org/packages/2d/af/4d7ece43c993369a8593c36e0f239b739b78c01e71d74553a630dadd1599/pyobjc_framework_IOSurface-11.0-py3-none-any.whl", hash = "sha256:f2bc13cbfd178396bde6e7558b05a49f69cce376885a07f645a5dd69d2b578fc", size = 4972, upload_time = "2025-01-14T18:54:03.244Z" }, + { url = "https://files.pythonhosted.org/packages/b8/08/b96f84b623e2dd2ef733ccdd67a1694f51bfdb4dfd81d38e7755566ab9e5/pyobjc_framework_IOSurface-11.0-py2.py3-none-any.whl", hash = "sha256:58c6e79401a00dc63a5797cd3cc067542d4f94fcd2fc8979dc248c3b06c3b829", size = 4905, upload-time = "2025-01-14T18:54:00.978Z" }, + { url = "https://files.pythonhosted.org/packages/2d/af/4d7ece43c993369a8593c36e0f239b739b78c01e71d74553a630dadd1599/pyobjc_framework_IOSurface-11.0-py3-none-any.whl", hash = "sha256:f2bc13cbfd178396bde6e7558b05a49f69cce376885a07f645a5dd69d2b578fc", size = 4972, upload-time = "2025-01-14T18:54:03.244Z" }, ] [[package]] @@ -3155,10 +3155,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647, upload_time = "2025-01-14T19:04:11.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647, upload-time = "2025-01-14T19:04:11.333Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/d2/52d1c71ec91ec299e1324658d023954cf62ce4c275155dc66cd298517ae2/pyobjc_framework_iTunesLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3836fccec315f5186e4b029b486fd18d4b1f24a4c2e73f2d9f3e157ee66d294d", size = 5147, upload_time = "2025-01-14T19:01:49.97Z" }, - { url = "https://files.pythonhosted.org/packages/dc/97/c23c522d506ae01740c04982a1db5861888056dc65d56876a2de0fc490bc/pyobjc_framework_iTunesLibrary-11.0-py3-none-any.whl", hash = "sha256:bfd40fde3f057318329e5fb6e256051eea3f6cd2e2adb9c1f1f51fcb87deb05a", size = 5210, upload_time = "2025-01-14T19:01:51.573Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d2/52d1c71ec91ec299e1324658d023954cf62ce4c275155dc66cd298517ae2/pyobjc_framework_iTunesLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3836fccec315f5186e4b029b486fd18d4b1f24a4c2e73f2d9f3e157ee66d294d", size = 5147, upload-time = "2025-01-14T19:01:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/dc/97/c23c522d506ae01740c04982a1db5861888056dc65d56876a2de0fc490bc/pyobjc_framework_iTunesLibrary-11.0-py3-none-any.whl", hash = "sha256:bfd40fde3f057318329e5fb6e256051eea3f6cd2e2adb9c1f1f51fcb87deb05a", size = 5210, upload-time = "2025-01-14T19:01:51.573Z" }, ] [[package]] @@ -3169,10 +3169,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794, upload_time = "2025-01-14T19:04:14.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794, upload-time = "2025-01-14T19:04:14.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/fe/ad7278325d8c760d5366b08d6162193612a3bf33bb0fa98d83d7dcc41918/pyobjc_framework_KernelManagement-11.0-py2.py3-none-any.whl", hash = "sha256:e2ad0efd00c0dce90fc05efac296733282c482d54ec7c5fdcb86b4fb8dff1eb8", size = 3604, upload_time = "2025-01-14T18:54:36.643Z" }, - { url = "https://files.pythonhosted.org/packages/1e/20/8aff6699bf780c88770214f72e92b9db736de078aa1aaaea45312758116e/pyobjc_framework_KernelManagement-11.0-py3-none-any.whl", hash = "sha256:90baacf8bea2883fd62ffb5d7dc6e6ae43fcc6f444458c884da8d92170fcaa5e", size = 3675, upload_time = "2025-01-14T18:54:37.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/fe/ad7278325d8c760d5366b08d6162193612a3bf33bb0fa98d83d7dcc41918/pyobjc_framework_KernelManagement-11.0-py2.py3-none-any.whl", hash = "sha256:e2ad0efd00c0dce90fc05efac296733282c482d54ec7c5fdcb86b4fb8dff1eb8", size = 3604, upload-time = "2025-01-14T18:54:36.643Z" }, + { url = "https://files.pythonhosted.org/packages/1e/20/8aff6699bf780c88770214f72e92b9db736de078aa1aaaea45312758116e/pyobjc_framework_KernelManagement-11.0-py3-none-any.whl", hash = "sha256:90baacf8bea2883fd62ffb5d7dc6e6ae43fcc6f444458c884da8d92170fcaa5e", size = 3675, upload-time = "2025-01-14T18:54:37.62Z" }, ] [[package]] @@ -3183,10 +3183,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704, upload_time = "2025-01-14T19:04:14.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704, upload-time = "2025-01-14T19:04:14.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/87/a8d2f508c021afa4f8af51773ab22cbd883270bfda8368a86d473736b05a/pyobjc_framework_LatentSemanticMapping-11.0-py2.py3-none-any.whl", hash = "sha256:87fd91320fb7ce0b2c482fda41a5c38388f5a694ee2d7208725d22ff75438c00", size = 5369, upload_time = "2025-01-14T18:54:38.493Z" }, - { url = "https://files.pythonhosted.org/packages/df/f0/cea2a0d25ad20aef6eb38c432d2c93bda2cb2239c6286b6086f8687a8072/pyobjc_framework_LatentSemanticMapping-11.0-py3-none-any.whl", hash = "sha256:073b8a4e7a22e6abd58005b7d7091144aec4fc1d4b519e9f972b3aee9da30009", size = 5435, upload_time = "2025-01-14T18:54:39.643Z" }, + { url = "https://files.pythonhosted.org/packages/7f/87/a8d2f508c021afa4f8af51773ab22cbd883270bfda8368a86d473736b05a/pyobjc_framework_LatentSemanticMapping-11.0-py2.py3-none-any.whl", hash = "sha256:87fd91320fb7ce0b2c482fda41a5c38388f5a694ee2d7208725d22ff75438c00", size = 5369, upload-time = "2025-01-14T18:54:38.493Z" }, + { url = "https://files.pythonhosted.org/packages/df/f0/cea2a0d25ad20aef6eb38c432d2c93bda2cb2239c6286b6086f8687a8072/pyobjc_framework_LatentSemanticMapping-11.0-py3-none-any.whl", hash = "sha256:073b8a4e7a22e6abd58005b7d7091144aec4fc1d4b519e9f972b3aee9da30009", size = 5435, upload-time = "2025-01-14T18:54:39.643Z" }, ] [[package]] @@ -3197,10 +3197,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-coreservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691, upload_time = "2025-01-14T19:04:15.884Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691, upload-time = "2025-01-14T19:04:15.884Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/46/72937390e3eb0f31809f0d56004a388d20b49724495885e8be677707c07c/pyobjc_framework_LaunchServices-11.0-py2.py3-none-any.whl", hash = "sha256:654572e5f2997d8f802b97f619fc6c7d4f927abb03ce53b3dad89b376517b2d1", size = 3807, upload_time = "2025-01-14T18:54:40.579Z" }, - { url = "https://files.pythonhosted.org/packages/c0/12/74b96f187beb2f5605f9d487c3141ac8d25193556f2f5febff3580e8b2cb/pyobjc_framework_LaunchServices-11.0-py3-none-any.whl", hash = "sha256:dbc169442deae53f881d1d07fc79c9da6459e5f0b411e8dd1cfd1c519b3a99c8", size = 3876, upload_time = "2025-01-14T18:54:41.577Z" }, + { url = "https://files.pythonhosted.org/packages/35/46/72937390e3eb0f31809f0d56004a388d20b49724495885e8be677707c07c/pyobjc_framework_LaunchServices-11.0-py2.py3-none-any.whl", hash = "sha256:654572e5f2997d8f802b97f619fc6c7d4f927abb03ce53b3dad89b376517b2d1", size = 3807, upload-time = "2025-01-14T18:54:40.579Z" }, + { url = "https://files.pythonhosted.org/packages/c0/12/74b96f187beb2f5605f9d487c3141ac8d25193556f2f5febff3580e8b2cb/pyobjc_framework_LaunchServices-11.0-py3-none-any.whl", hash = "sha256:dbc169442deae53f881d1d07fc79c9da6459e5f0b411e8dd1cfd1c519b3a99c8", size = 3876, upload-time = "2025-01-14T18:54:41.577Z" }, ] [[package]] @@ -3211,10 +3211,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496, upload_time = "2025-01-14T19:04:16.82Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496, upload-time = "2025-01-14T19:04:16.82Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/1f/f3273cc8261d45a6bef1fa48ac39cd94f6a1e77b1ec70f79bae52ad54015/pyobjc_framework_libdispatch-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cebdc33a1a771c9ab03fe5c8a2b0ed9698804e7bccdbfcd3cc0045c4b4aad4f3", size = 20607, upload_time = "2025-01-14T19:01:53.577Z" }, - { url = "https://files.pythonhosted.org/packages/32/08/40638a5e916b1b94b4b29abacb18628fd47871d80fdf2fc1ef7216726d29/pyobjc_framework_libdispatch-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:999815af50ad2216e28d76893023b7839b7f1e8f22bcf7062d81d9a51ade4613", size = 15949, upload_time = "2025-01-14T19:01:54.496Z" }, + { url = "https://files.pythonhosted.org/packages/24/1f/f3273cc8261d45a6bef1fa48ac39cd94f6a1e77b1ec70f79bae52ad54015/pyobjc_framework_libdispatch-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cebdc33a1a771c9ab03fe5c8a2b0ed9698804e7bccdbfcd3cc0045c4b4aad4f3", size = 20607, upload-time = "2025-01-14T19:01:53.577Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/40638a5e916b1b94b4b29abacb18628fd47871d80fdf2fc1ef7216726d29/pyobjc_framework_libdispatch-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:999815af50ad2216e28d76893023b7839b7f1e8f22bcf7062d81d9a51ade4613", size = 15949, upload-time = "2025-01-14T19:01:54.496Z" }, ] [[package]] @@ -3225,10 +3225,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627, upload_time = "2025-01-14T19:04:17.728Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627, upload-time = "2025-01-14T19:04:17.728Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/c2/b77019e344b3f46ca4169c19e0539cff9586c8db0a97715590696993bd00/pyobjc_framework_libxpc-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5f05f9eb3662df5832ff09ab788d6f6099f4674cb015200db317ea8c69f8c5e8", size = 19683, upload_time = "2025-01-14T19:02:02.364Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b9/bf34709c2d8f62a029f4c8e7f9a58c6eb5f3a68542cbcd2a15070b66485a/pyobjc_framework_libxpc-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e000cad8588a961a3e6e5016736cd76b5d992b080cfe8b95745691db5a0ce8df", size = 19788, upload_time = "2025-01-14T19:02:03.327Z" }, + { url = "https://files.pythonhosted.org/packages/21/c2/b77019e344b3f46ca4169c19e0539cff9586c8db0a97715590696993bd00/pyobjc_framework_libxpc-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5f05f9eb3662df5832ff09ab788d6f6099f4674cb015200db317ea8c69f8c5e8", size = 19683, upload-time = "2025-01-14T19:02:02.364Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b9/bf34709c2d8f62a029f4c8e7f9a58c6eb5f3a68542cbcd2a15070b66485a/pyobjc_framework_libxpc-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e000cad8588a961a3e6e5016736cd76b5d992b080cfe8b95745691db5a0ce8df", size = 19788, upload-time = "2025-01-14T19:02:03.327Z" }, ] [[package]] @@ -3240,10 +3240,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231, upload_time = "2025-01-14T19:04:20.763Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231, upload-time = "2025-01-14T19:04:20.763Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/fc/aa3f0016e2246c4574cce0e323416303992411a012266b5bdda74095ebef/pyobjc_framework_LinkPresentation-11.0-py2.py3-none-any.whl", hash = "sha256:c10ee1ac48bb7cd2d67ade7f354ec71af1f4244a8deb8530ba646fd4ba327b21", size = 3799, upload_time = "2025-01-14T18:54:43.137Z" }, - { url = "https://files.pythonhosted.org/packages/85/0b/77c16f2d4541a4490723e18c03c3bd6ecf7db789cf4988e628753e2e4526/pyobjc_framework_LinkPresentation-11.0-py3-none-any.whl", hash = "sha256:5b063900715c5bcf58f533e6c9672473cb07fe3eaa0f0454d93947defa09f13e", size = 3865, upload_time = "2025-01-14T18:54:44.287Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/aa3f0016e2246c4574cce0e323416303992411a012266b5bdda74095ebef/pyobjc_framework_LinkPresentation-11.0-py2.py3-none-any.whl", hash = "sha256:c10ee1ac48bb7cd2d67ade7f354ec71af1f4244a8deb8530ba646fd4ba327b21", size = 3799, upload-time = "2025-01-14T18:54:43.137Z" }, + { url = "https://files.pythonhosted.org/packages/85/0b/77c16f2d4541a4490723e18c03c3bd6ecf7db789cf4988e628753e2e4526/pyobjc_framework_LinkPresentation-11.0-py3-none-any.whl", hash = "sha256:5b063900715c5bcf58f533e6c9672473cb07fe3eaa0f0454d93947defa09f13e", size = 3865, upload-time = "2025-01-14T18:54:44.287Z" }, ] [[package]] @@ -3255,10 +3255,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-security" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020, upload_time = "2025-01-14T19:04:22.206Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020, upload-time = "2025-01-14T19:04:22.206Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/dd/eaa44e4fe3b5c312190c0468afcab0a4372da29535fe9f860b6b9e1e6b4a/pyobjc_framework_LocalAuthentication-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6500bb5b195799d70f2a622d89a9c2531cb13d6afe30916cf073a195dd86eb", size = 10515, upload_time = "2025-01-14T18:54:46.214Z" }, - { url = "https://files.pythonhosted.org/packages/31/86/f4e913e966a6dbefbaa95aed35e7d235ba2f172d079d3c0b4351a584357b/pyobjc_framework_LocalAuthentication-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c0291e743fb1534c1df900e9adacc809af0651744627ce8ae25cfd021e3db73b", size = 10530, upload_time = "2025-01-14T18:54:47.16Z" }, + { url = "https://files.pythonhosted.org/packages/d1/dd/eaa44e4fe3b5c312190c0468afcab0a4372da29535fe9f860b6b9e1e6b4a/pyobjc_framework_LocalAuthentication-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6500bb5b195799d70f2a622d89a9c2531cb13d6afe30916cf073a195dd86eb", size = 10515, upload-time = "2025-01-14T18:54:46.214Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/f4e913e966a6dbefbaa95aed35e7d235ba2f172d079d3c0b4351a584357b/pyobjc_framework_LocalAuthentication-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c0291e743fb1534c1df900e9adacc809af0651744627ce8ae25cfd021e3db73b", size = 10530, upload-time = "2025-01-14T18:54:47.16Z" }, ] [[package]] @@ -3270,10 +3270,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-localauthentication" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204, upload_time = "2025-01-14T19:04:23.566Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204, upload-time = "2025-01-14T19:04:23.566Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/66/2151e5ee7fb97b34c7eda9f8b1442683cced27bcb273d34c8aa2c564e528/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py2.py3-none-any.whl", hash = "sha256:0ccbbdd8c7142b1670885881c803f684ee356df83a5338be9135f46462caae6c", size = 3914, upload_time = "2025-01-14T18:54:52.074Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a9/c362ac3586bb2d46868b8ea9da3747c9aae3f0c9448ee09934a1be805383/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py3-none-any.whl", hash = "sha256:e8da98dc38a88995e344742585d3735af9b5bd9926a29774d77e2aa6dd46b7af", size = 3984, upload_time = "2025-01-14T18:54:54.974Z" }, + { url = "https://files.pythonhosted.org/packages/da/66/2151e5ee7fb97b34c7eda9f8b1442683cced27bcb273d34c8aa2c564e528/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py2.py3-none-any.whl", hash = "sha256:0ccbbdd8c7142b1670885881c803f684ee356df83a5338be9135f46462caae6c", size = 3914, upload-time = "2025-01-14T18:54:52.074Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a9/c362ac3586bb2d46868b8ea9da3747c9aae3f0c9448ee09934a1be805383/pyobjc_framework_LocalAuthenticationEmbeddedUI-11.0-py3-none-any.whl", hash = "sha256:e8da98dc38a88995e344742585d3735af9b5bd9926a29774d77e2aa6dd46b7af", size = 3984, upload-time = "2025-01-14T18:54:54.974Z" }, ] [[package]] @@ -3284,10 +3284,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274, upload_time = "2025-01-14T19:04:25.086Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274, upload-time = "2025-01-14T19:04:25.086Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/38/f9bcd204c1ba0943365f3cc505d934ea93fe4b99d61e961ced0f0991a4f9/pyobjc_framework_MailKit-11.0-py2.py3-none-any.whl", hash = "sha256:78e54ff3988fd1af16c06e0c39dea3b7ff522e367d262f58e88962772291c7f9", size = 4803, upload_time = "2025-01-14T18:54:58.295Z" }, - { url = "https://files.pythonhosted.org/packages/64/4a/f3596583795c608838c7fa84fc4836f365c5744a3e412392d47a200a6221/pyobjc_framework_MailKit-11.0-py3-none-any.whl", hash = "sha256:0573ee0be66419130774aca36b611d0d07fcf7c756524860acba8fe17eefeec2", size = 4874, upload_time = "2025-01-14T18:55:00.648Z" }, + { url = "https://files.pythonhosted.org/packages/d2/38/f9bcd204c1ba0943365f3cc505d934ea93fe4b99d61e961ced0f0991a4f9/pyobjc_framework_MailKit-11.0-py2.py3-none-any.whl", hash = "sha256:78e54ff3988fd1af16c06e0c39dea3b7ff522e367d262f58e88962772291c7f9", size = 4803, upload-time = "2025-01-14T18:54:58.295Z" }, + { url = "https://files.pythonhosted.org/packages/64/4a/f3596583795c608838c7fa84fc4836f365c5744a3e412392d47a200a6221/pyobjc_framework_MailKit-11.0-py3-none-any.whl", hash = "sha256:0573ee0be66419130774aca36b611d0d07fcf7c756524860acba8fe17eefeec2", size = 4874, upload-time = "2025-01-14T18:55:00.648Z" }, ] [[package]] @@ -3300,10 +3300,10 @@ dependencies = [ { name = "pyobjc-framework-corelocation" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080, upload_time = "2025-01-14T19:04:26.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080, upload-time = "2025-01-14T19:04:26.606Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/7e/f0457c7ca001a01f47aa944c1f86a24d2d04db0aa1c19f51cbf77a65cc9b/pyobjc_framework_MapKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83128d79aa7644e5b966b32346f7da749b1dbb110dadba857b93ecf5663e24e6", size = 23045, upload_time = "2025-01-14T18:55:02.629Z" }, - { url = "https://files.pythonhosted.org/packages/d5/b0/532b4f57f8783cf6394b17e76174c393d0503ee41e026782a9950bd46279/pyobjc_framework_MapKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e6aa1d00cfe2e02b301467e24ca51e469e9a8a2ec2a9f097b73adca1a5a2a054", size = 23040, upload_time = "2025-01-14T18:55:04.055Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7e/f0457c7ca001a01f47aa944c1f86a24d2d04db0aa1c19f51cbf77a65cc9b/pyobjc_framework_MapKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83128d79aa7644e5b966b32346f7da749b1dbb110dadba857b93ecf5663e24e6", size = 23045, upload-time = "2025-01-14T18:55:02.629Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b0/532b4f57f8783cf6394b17e76174c393d0503ee41e026782a9950bd46279/pyobjc_framework_MapKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e6aa1d00cfe2e02b301467e24ca51e469e9a8a2ec2a9f097b73adca1a5a2a054", size = 23040, upload-time = "2025-01-14T18:55:04.055Z" }, ] [[package]] @@ -3314,10 +3314,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671, upload_time = "2025-01-14T19:04:27.624Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671, upload-time = "2025-01-14T19:04:27.624Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/1f/36b1115cfd02d68d39cc3fe976fe3d40bad1d1a0a9c8175c66d230bb7276/pyobjc_framework_MediaAccessibility-11.0-py2.py3-none-any.whl", hash = "sha256:901961f171f7af184decbf5a3899debfa56dbd1a63a53d0ff3d93eff90f2f464", size = 4637, upload_time = "2025-01-14T18:55:08.968Z" }, - { url = "https://files.pythonhosted.org/packages/72/3f/fa350681a6599ed6756dc598fcd17fda1521249e4570a57b4a9b9c900f47/pyobjc_framework_MediaAccessibility-11.0-py3-none-any.whl", hash = "sha256:3f4b9e4d1ac8e7f8cdb7a2e9839ab75cb358dead3e6365ccd8d6017d7e93811e", size = 4708, upload_time = "2025-01-14T18:55:09.939Z" }, + { url = "https://files.pythonhosted.org/packages/50/1f/36b1115cfd02d68d39cc3fe976fe3d40bad1d1a0a9c8175c66d230bb7276/pyobjc_framework_MediaAccessibility-11.0-py2.py3-none-any.whl", hash = "sha256:901961f171f7af184decbf5a3899debfa56dbd1a63a53d0ff3d93eff90f2f464", size = 4637, upload-time = "2025-01-14T18:55:08.968Z" }, + { url = "https://files.pythonhosted.org/packages/72/3f/fa350681a6599ed6756dc598fcd17fda1521249e4570a57b4a9b9c900f47/pyobjc_framework_MediaAccessibility-11.0-py3-none-any.whl", hash = "sha256:3f4b9e4d1ac8e7f8cdb7a2e9839ab75cb358dead3e6365ccd8d6017d7e93811e", size = 4708, upload-time = "2025-01-14T18:55:09.939Z" }, ] [[package]] @@ -3330,10 +3330,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coremedia" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931, upload_time = "2025-01-14T19:04:28.65Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931, upload-time = "2025-01-14T19:04:28.65Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/94/1e4aa67e424a043dfa886c946bb872f9653cc12ad59bd7c2c24e3d19a4f5/pyobjc_framework_MediaExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f25d674f381bae800761efe1628959293009d287f7127616f75318a87e4543d", size = 39781, upload_time = "2025-01-14T18:55:13.454Z" }, - { url = "https://files.pythonhosted.org/packages/02/3c/2cbd4498950daadd111639a7b8dea2aaa6825526677b31ae49bc940f1036/pyobjc_framework_MediaExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9a167725f7a6921d446084b132505392bb375a5ef91498f7be5d94c0d48d26ae", size = 39777, upload_time = "2025-01-14T18:55:14.434Z" }, + { url = "https://files.pythonhosted.org/packages/4d/94/1e4aa67e424a043dfa886c946bb872f9653cc12ad59bd7c2c24e3d19a4f5/pyobjc_framework_MediaExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f25d674f381bae800761efe1628959293009d287f7127616f75318a87e4543d", size = 39781, upload-time = "2025-01-14T18:55:13.454Z" }, + { url = "https://files.pythonhosted.org/packages/02/3c/2cbd4498950daadd111639a7b8dea2aaa6825526677b31ae49bc940f1036/pyobjc_framework_MediaExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9a167725f7a6921d446084b132505392bb375a5ef91498f7be5d94c0d48d26ae", size = 39777, upload-time = "2025-01-14T18:55:14.434Z" }, ] [[package]] @@ -3345,10 +3345,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975, upload_time = "2025-01-14T19:04:29.739Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975, upload-time = "2025-01-14T19:04:29.739Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/b6/c079b41a7a4b6b856b4ba7196500f058fb9d9f4f021269b49cf0861ace1f/pyobjc_framework_MediaLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3d273d4db7e1894fd2a95448c26eeced6e13e33555f727988aeec4b2762246fb", size = 4288, upload_time = "2025-01-14T18:55:20.473Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ae/05f2ee15f5e8524b27d6e446822edfed977c1ed0d3201644ae4d5d78bdde/pyobjc_framework_MediaLibrary-11.0-py3-none-any.whl", hash = "sha256:b8b97bb9067cf81942ce69d3273e2b18d093290c3fd692172a54f012ab64c0b3", size = 4359, upload_time = "2025-01-14T18:55:21.491Z" }, + { url = "https://files.pythonhosted.org/packages/16/b6/c079b41a7a4b6b856b4ba7196500f058fb9d9f4f021269b49cf0861ace1f/pyobjc_framework_MediaLibrary-11.0-py2.py3-none-any.whl", hash = "sha256:3d273d4db7e1894fd2a95448c26eeced6e13e33555f727988aeec4b2762246fb", size = 4288, upload-time = "2025-01-14T18:55:20.473Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/05f2ee15f5e8524b27d6e446822edfed977c1ed0d3201644ae4d5d78bdde/pyobjc_framework_MediaLibrary-11.0-py3-none-any.whl", hash = "sha256:b8b97bb9067cf81942ce69d3273e2b18d093290c3fd692172a54f012ab64c0b3", size = 4359, upload-time = "2025-01-14T18:55:21.491Z" }, ] [[package]] @@ -3359,10 +3359,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-avfoundation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020, upload_time = "2025-01-14T19:04:30.617Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020, upload-time = "2025-01-14T19:04:30.617Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/b2/57b7b75bb5f2b624ce48cd48fb7d651d2f24d279918b352ae8fb03384b47/pyobjc_framework_MediaPlayer-11.0-py2.py3-none-any.whl", hash = "sha256:b124b0f18444b69b64142bad2579287d0b1a4a35cb6b14526523a822066d527d", size = 6903, upload_time = "2025-01-14T18:55:24.375Z" }, - { url = "https://files.pythonhosted.org/packages/e9/8e/4969374f0fb243dd06336f2edc8c755743a683e73a57c3253279d048a455/pyobjc_framework_MediaPlayer-11.0-py3-none-any.whl", hash = "sha256:1a051624b536666feb5fd1a4bb54000ab45dac0c8aea4cd4707cbde1773acf57", size = 6977, upload_time = "2025-01-14T18:55:25.359Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/57b7b75bb5f2b624ce48cd48fb7d651d2f24d279918b352ae8fb03384b47/pyobjc_framework_MediaPlayer-11.0-py2.py3-none-any.whl", hash = "sha256:b124b0f18444b69b64142bad2579287d0b1a4a35cb6b14526523a822066d527d", size = 6903, upload-time = "2025-01-14T18:55:24.375Z" }, + { url = "https://files.pythonhosted.org/packages/e9/8e/4969374f0fb243dd06336f2edc8c755743a683e73a57c3253279d048a455/pyobjc_framework_MediaPlayer-11.0-py3-none-any.whl", hash = "sha256:1a051624b536666feb5fd1a4bb54000ab45dac0c8aea4cd4707cbde1773acf57", size = 6977, upload-time = "2025-01-14T18:55:25.359Z" }, ] [[package]] @@ -3373,10 +3373,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067, upload_time = "2025-01-14T19:04:32.823Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067, upload-time = "2025-01-14T19:04:32.823Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/d5/ee184e33bd743c363d7ab59d8412289c6ac14c78a035545a067b98704ae2/pyobjc_framework_MediaToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df09e4db52d4efeafe4a324600b9c5062fd87c1d1217ebec2df65c8b6b0ce9ef", size = 12776, upload_time = "2025-01-14T18:55:30.277Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a5/c02d2c44ebcd5884d7ccf55c597c0960d14e4e8f386b65dcd76f9f50ec3d/pyobjc_framework_MediaToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e80e3057f5030fb034ac93c3e891cee346716e1669f280ebbd63ccfa52b2b7ff", size = 12937, upload_time = "2025-01-14T18:55:34.139Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d5/ee184e33bd743c363d7ab59d8412289c6ac14c78a035545a067b98704ae2/pyobjc_framework_MediaToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df09e4db52d4efeafe4a324600b9c5062fd87c1d1217ebec2df65c8b6b0ce9ef", size = 12776, upload-time = "2025-01-14T18:55:30.277Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a5/c02d2c44ebcd5884d7ccf55c597c0960d14e4e8f386b65dcd76f9f50ec3d/pyobjc_framework_MediaToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e80e3057f5030fb034ac93c3e891cee346716e1669f280ebbd63ccfa52b2b7ff", size = 12937, upload-time = "2025-01-14T18:55:34.139Z" }, ] [[package]] @@ -3387,10 +3387,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102, upload_time = "2025-01-14T19:04:34.011Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102, upload-time = "2025-01-14T19:04:34.011Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/fe/083727028e63ffcf7455d10288df05696737ee74a31decdc671e32624f58/pyobjc_framework_Metal-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ac5f317d52cd7523dea2e172fbe8b03e7452b907da42a0a5e5c5ab427c5e9de", size = 57321, upload_time = "2025-01-14T18:55:39.025Z" }, - { url = "https://files.pythonhosted.org/packages/78/85/396ad46929ec6e2aa554c29a3fae2f7c7ffb2e1a3fbb9c41948d5a573dc8/pyobjc_framework_Metal-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45802d48d1a35cc66fee08539c8ca9fc6a0dc4ab700cf78a81cf5f8982ed6f5b", size = 57099, upload_time = "2025-01-14T18:55:40.091Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fe/083727028e63ffcf7455d10288df05696737ee74a31decdc671e32624f58/pyobjc_framework_Metal-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ac5f317d52cd7523dea2e172fbe8b03e7452b907da42a0a5e5c5ab427c5e9de", size = 57321, upload-time = "2025-01-14T18:55:39.025Z" }, + { url = "https://files.pythonhosted.org/packages/78/85/396ad46929ec6e2aa554c29a3fae2f7c7ffb2e1a3fbb9c41948d5a573dc8/pyobjc_framework_Metal-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45802d48d1a35cc66fee08539c8ca9fc6a0dc4ab700cf78a81cf5f8982ed6f5b", size = 57099, upload-time = "2025-01-14T18:55:40.091Z" }, ] [[package]] @@ -3401,10 +3401,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436, upload_time = "2025-01-14T19:04:36.161Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436, upload-time = "2025-01-14T19:04:36.161Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/f1/4140b63b3128cb2f12e136c4158a082ce170e4eb979bccb628768c59fd98/pyobjc_framework_MetalFX-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3a3847812d40cb6bb7a5f0e735f9f28cba83a1e1264d4dafc630ce894e98a20", size = 10308, upload_time = "2025-01-14T18:55:47.719Z" }, - { url = "https://files.pythonhosted.org/packages/c0/85/460abd4f96a7a3efd36404a480ed4d31a51f4b3ed64dc4595502a5f725c3/pyobjc_framework_MetalFX-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a37dc271513b217fcba4a99c6cd92997ee171b49b974e0a9dd1b35feb32b7109", size = 10338, upload_time = "2025-01-14T18:55:48.593Z" }, + { url = "https://files.pythonhosted.org/packages/16/f1/4140b63b3128cb2f12e136c4158a082ce170e4eb979bccb628768c59fd98/pyobjc_framework_MetalFX-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3a3847812d40cb6bb7a5f0e735f9f28cba83a1e1264d4dafc630ce894e98a20", size = 10308, upload-time = "2025-01-14T18:55:47.719Z" }, + { url = "https://files.pythonhosted.org/packages/c0/85/460abd4f96a7a3efd36404a480ed4d31a51f4b3ed64dc4595502a5f725c3/pyobjc_framework_MetalFX-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a37dc271513b217fcba4a99c6cd92997ee171b49b974e0a9dd1b35feb32b7109", size = 10338, upload-time = "2025-01-14T18:55:48.593Z" }, ] [[package]] @@ -3416,10 +3416,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628, upload_time = "2025-01-14T19:04:36.977Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628, upload-time = "2025-01-14T19:04:36.977Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/44/e7eb6746d9e1ad0ad08ab0a8ac20d264b049960363a8f28a744d1d9c319c/pyobjc_framework_MetalKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f314478a5d772d2f7b4db09957ecb63acd6e3f0cde8c18b1b6b35caa9ea7def2", size = 8598, upload_time = "2025-01-14T18:55:56.761Z" }, - { url = "https://files.pythonhosted.org/packages/a6/1c/1ae6d629065e495e8e0b7def36e1d632e461a933f616f9776a914d69b2fd/pyobjc_framework_MetalKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f2d93180e7ac5abd906e492165a72f82d308d68101eadd213bba68a4b1dc4a8", size = 8611, upload_time = "2025-01-14T18:55:58.085Z" }, + { url = "https://files.pythonhosted.org/packages/d3/44/e7eb6746d9e1ad0ad08ab0a8ac20d264b049960363a8f28a744d1d9c319c/pyobjc_framework_MetalKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f314478a5d772d2f7b4db09957ecb63acd6e3f0cde8c18b1b6b35caa9ea7def2", size = 8598, upload-time = "2025-01-14T18:55:56.761Z" }, + { url = "https://files.pythonhosted.org/packages/a6/1c/1ae6d629065e495e8e0b7def36e1d632e461a933f616f9776a914d69b2fd/pyobjc_framework_MetalKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f2d93180e7ac5abd906e492165a72f82d308d68101eadd213bba68a4b1dc4a8", size = 8611, upload-time = "2025-01-14T18:55:58.085Z" }, ] [[package]] @@ -3430,10 +3430,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-metal" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444, upload_time = "2025-01-14T19:04:38.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444, upload-time = "2025-01-14T19:04:38.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/e9/3741ac0e745e1014961f12cf967eac1d4ec5b110d3ed13fdf9dd4ce27933/pyobjc_framework_MetalPerformanceShaders-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:460a30ff31f04bbe82bf3304949171e148e3171ba0c0773dd9bfc42dad766d2e", size = 33004, upload_time = "2025-01-14T18:56:02.993Z" }, - { url = "https://files.pythonhosted.org/packages/39/b4/51434a9a897a47f6a0d1f6079725e3de4dbc75a7004275f116a2043cf80b/pyobjc_framework_MetalPerformanceShaders-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abd4649de32aedfa45f8535d74227ba3e1411b6426f794026e8426feab43ea8e", size = 33222, upload_time = "2025-01-14T18:56:04.78Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e9/3741ac0e745e1014961f12cf967eac1d4ec5b110d3ed13fdf9dd4ce27933/pyobjc_framework_MetalPerformanceShaders-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:460a30ff31f04bbe82bf3304949171e148e3171ba0c0773dd9bfc42dad766d2e", size = 33004, upload-time = "2025-01-14T18:56:02.993Z" }, + { url = "https://files.pythonhosted.org/packages/39/b4/51434a9a897a47f6a0d1f6079725e3de4dbc75a7004275f116a2043cf80b/pyobjc_framework_MetalPerformanceShaders-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abd4649de32aedfa45f8535d74227ba3e1411b6426f794026e8426feab43ea8e", size = 33222, upload-time = "2025-01-14T18:56:04.78Z" }, ] [[package]] @@ -3444,10 +3444,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-metalperformanceshaders" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381, upload_time = "2025-01-14T19:04:39.831Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381, upload-time = "2025-01-14T19:04:39.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/8c/3d8f1cc6cfe7f9fd73f3911bb62256fdefc4d7f5375b8be84870d8c15650/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py2.py3-none-any.whl", hash = "sha256:d48ffe401fbc8273a23e908685635a51c64d4ebfb5ad32742664ab9fac6c5194", size = 6403, upload_time = "2025-01-14T18:56:10.236Z" }, - { url = "https://files.pythonhosted.org/packages/ef/26/ca0441ac11d5ecc7814b48b3af9df467ead93622f0edc67e947f1a4afe97/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py3-none-any.whl", hash = "sha256:f0702a6e91b273e552283ff2782220ce08eb65325aa45ad428e0b7f3b45cf211", size = 6474, upload_time = "2025-01-14T18:56:11.479Z" }, + { url = "https://files.pythonhosted.org/packages/0d/8c/3d8f1cc6cfe7f9fd73f3911bb62256fdefc4d7f5375b8be84870d8c15650/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py2.py3-none-any.whl", hash = "sha256:d48ffe401fbc8273a23e908685635a51c64d4ebfb5ad32742664ab9fac6c5194", size = 6403, upload-time = "2025-01-14T18:56:10.236Z" }, + { url = "https://files.pythonhosted.org/packages/ef/26/ca0441ac11d5ecc7814b48b3af9df467ead93622f0edc67e947f1a4afe97/pyobjc_framework_MetalPerformanceShadersGraph-11.0-py3-none-any.whl", hash = "sha256:f0702a6e91b273e552283ff2782220ce08eb65325aa45ad428e0b7f3b45cf211", size = 6474, upload-time = "2025-01-14T18:56:11.479Z" }, ] [[package]] @@ -3458,10 +3458,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414, upload_time = "2025-01-14T19:04:41.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414, upload-time = "2025-01-14T19:04:41.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/1f/cc897b07b3ed96a26a3008f43e0deefaa60e280ac13118a2ff4224fca0d8/pyobjc_framework_MetricKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:422b6ca1f082dae864df8cc4aa0bac3829be95675b72ef63cd3ee00d30966b97", size = 7958, upload_time = "2025-01-14T18:56:13.428Z" }, - { url = "https://files.pythonhosted.org/packages/19/63/f37010479670958d3c976d007d45107c3fc53b5626586527c6310821e15a/pyobjc_framework_MetricKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b94313601bbf0181c8f75712e82646261ff0e020da5c83d25914952db53a7955", size = 7966, upload_time = "2025-01-14T18:56:14.36Z" }, + { url = "https://files.pythonhosted.org/packages/13/1f/cc897b07b3ed96a26a3008f43e0deefaa60e280ac13118a2ff4224fca0d8/pyobjc_framework_MetricKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:422b6ca1f082dae864df8cc4aa0bac3829be95675b72ef63cd3ee00d30966b97", size = 7958, upload-time = "2025-01-14T18:56:13.428Z" }, + { url = "https://files.pythonhosted.org/packages/19/63/f37010479670958d3c976d007d45107c3fc53b5626586527c6310821e15a/pyobjc_framework_MetricKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b94313601bbf0181c8f75712e82646261ff0e020da5c83d25914952db53a7955", size = 7966, upload-time = "2025-01-14T18:56:14.36Z" }, ] [[package]] @@ -3472,10 +3472,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265, upload_time = "2025-01-14T19:04:43.326Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265, upload-time = "2025-01-14T19:04:43.326Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/06/a5865c0e4db4e7289bf6b40242b7149af87d5779f34ca168df5cabf2d5a4/pyobjc_framework_MLCompute-11.0-py2.py3-none-any.whl", hash = "sha256:16ec2942af9915f931df76b42e7f42348109b599faef955f5bea540735f87677", size = 6729, upload_time = "2025-01-14T18:54:55.927Z" }, - { url = "https://files.pythonhosted.org/packages/b5/15/3c69df5b5b99cea4a573e1d0e3c0b607cfe4ea1404ea1fe3a302361eb452/pyobjc_framework_MLCompute-11.0-py3-none-any.whl", hash = "sha256:bcdf94fe060fb034aed41db84af1cfcdbf3925e69b2b11df89d4546fac6cf0bf", size = 6799, upload_time = "2025-01-14T18:54:56.893Z" }, + { url = "https://files.pythonhosted.org/packages/75/06/a5865c0e4db4e7289bf6b40242b7149af87d5779f34ca168df5cabf2d5a4/pyobjc_framework_MLCompute-11.0-py2.py3-none-any.whl", hash = "sha256:16ec2942af9915f931df76b42e7f42348109b599faef955f5bea540735f87677", size = 6729, upload-time = "2025-01-14T18:54:55.927Z" }, + { url = "https://files.pythonhosted.org/packages/b5/15/3c69df5b5b99cea4a573e1d0e3c0b607cfe4ea1404ea1fe3a302361eb452/pyobjc_framework_MLCompute-11.0-py3-none-any.whl", hash = "sha256:bcdf94fe060fb034aed41db84af1cfcdbf3925e69b2b11df89d4546fac6cf0bf", size = 6799, upload-time = "2025-01-14T18:54:56.893Z" }, ] [[package]] @@ -3487,10 +3487,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652, upload_time = "2025-01-14T19:04:44.263Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652, upload-time = "2025-01-14T19:04:44.263Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/98/a30e8df5624c7929dfcd9748bf859929e8aa2c7d836efe5888dafc05f729/pyobjc_framework_ModelIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c126318b878ffb31c39b0c7c91ca20a3b46c14c18f000e3bfb854e4541fe0147", size = 20715, upload_time = "2025-01-14T18:56:22.163Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f8/bb4bc635eb16331c20731cae2e495645d0d10e25962451631eb9085a3f85/pyobjc_framework_ModelIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a7357f07b77f3ab0a8107d827acdbc3e1fd458ce396335c057930b6a3f225a93", size = 20715, upload_time = "2025-01-14T18:56:23.297Z" }, + { url = "https://files.pythonhosted.org/packages/11/98/a30e8df5624c7929dfcd9748bf859929e8aa2c7d836efe5888dafc05f729/pyobjc_framework_ModelIO-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c126318b878ffb31c39b0c7c91ca20a3b46c14c18f000e3bfb854e4541fe0147", size = 20715, upload-time = "2025-01-14T18:56:22.163Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f8/bb4bc635eb16331c20731cae2e495645d0d10e25962451631eb9085a3f85/pyobjc_framework_ModelIO-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a7357f07b77f3ab0a8107d827acdbc3e1fd458ce396335c057930b6a3f225a93", size = 20715, upload-time = "2025-01-14T18:56:23.297Z" }, ] [[package]] @@ -3501,10 +3501,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534, upload_time = "2025-01-14T19:04:45.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534, upload-time = "2025-01-14T19:04:45.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/d2/a4144f966cbe998f8da46b936783561bcd3e7e84b8f2dc45eb49ee3f6f21/pyobjc_framework_MultipeerConnectivity-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e338b22f5b0fcb398e316552398c252bedfc3375c058340861eb205e3cdf994e", size = 12423, upload_time = "2025-01-14T18:56:30.132Z" }, - { url = "https://files.pythonhosted.org/packages/7b/50/ac9213aca34d30993a36525c23d19ba5a568d3ea4e31e3bc2a6940ddafde/pyobjc_framework_MultipeerConnectivity-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66bef15f5e5afd6b966cdadf2162082b0171f4a45af6d2cb2644f38431011911", size = 12447, upload_time = "2025-01-14T18:56:31.04Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d2/a4144f966cbe998f8da46b936783561bcd3e7e84b8f2dc45eb49ee3f6f21/pyobjc_framework_MultipeerConnectivity-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e338b22f5b0fcb398e316552398c252bedfc3375c058340861eb205e3cdf994e", size = 12423, upload-time = "2025-01-14T18:56:30.132Z" }, + { url = "https://files.pythonhosted.org/packages/7b/50/ac9213aca34d30993a36525c23d19ba5a568d3ea4e31e3bc2a6940ddafde/pyobjc_framework_MultipeerConnectivity-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66bef15f5e5afd6b966cdadf2162082b0171f4a45af6d2cb2644f38431011911", size = 12447, upload-time = "2025-01-14T18:56:31.04Z" }, ] [[package]] @@ -3515,10 +3515,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385, upload_time = "2025-01-14T19:04:46.185Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385, upload-time = "2025-01-14T19:04:46.185Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/72/2246c0a6dc2d087951a626157f52c81cf88fe28393994163e9572fd1eb61/pyobjc_framework_NaturalLanguage-11.0-py2.py3-none-any.whl", hash = "sha256:0744a2871690dcc9ec9e7169023b492abdde63ef97abde46013c01477b4d047c", size = 5250, upload_time = "2025-01-14T18:56:34.675Z" }, - { url = "https://files.pythonhosted.org/packages/3a/49/f5faf3fab0f1ffb21882115878f1e5023257239aa576d6c01c31e42dd1da/pyobjc_framework_NaturalLanguage-11.0-py3-none-any.whl", hash = "sha256:7c021b270fda5469b56b9804e860cf5a80a485b817fc5fd3bb002383b2982d94", size = 5321, upload_time = "2025-01-14T18:56:36.377Z" }, + { url = "https://files.pythonhosted.org/packages/7d/72/2246c0a6dc2d087951a626157f52c81cf88fe28393994163e9572fd1eb61/pyobjc_framework_NaturalLanguage-11.0-py2.py3-none-any.whl", hash = "sha256:0744a2871690dcc9ec9e7169023b492abdde63ef97abde46013c01477b4d047c", size = 5250, upload-time = "2025-01-14T18:56:34.675Z" }, + { url = "https://files.pythonhosted.org/packages/3a/49/f5faf3fab0f1ffb21882115878f1e5023257239aa576d6c01c31e42dd1da/pyobjc_framework_NaturalLanguage-11.0-py3-none-any.whl", hash = "sha256:7c021b270fda5469b56b9804e860cf5a80a485b817fc5fd3bb002383b2982d94", size = 5321, upload-time = "2025-01-14T18:56:36.377Z" }, ] [[package]] @@ -3529,10 +3529,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173, upload_time = "2025-01-14T19:04:47.11Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173, upload-time = "2025-01-14T19:04:47.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/e7/4be35bc2adbebffb5ac7ede2b8459432194a82bd8f325af12b77b7c26248/pyobjc_framework_NetFS-11.0-py2.py3-none-any.whl", hash = "sha256:11e06da73a1d590b8462f3a1412604758d49b5e04d134b6e991282453b76abb8", size = 4088, upload_time = "2025-01-14T18:56:37.646Z" }, - { url = "https://files.pythonhosted.org/packages/fe/83/b7c8dfaee82c0312af25c2b31621505ce19f01fab7bb55eec69c0b4d24ad/pyobjc_framework_NetFS-11.0-py3-none-any.whl", hash = "sha256:9b69a36e3a6782ce37cd3140c584dd7d5c96f7355662d004a2927583b112b4dd", size = 4162, upload_time = "2025-01-14T18:56:38.682Z" }, + { url = "https://files.pythonhosted.org/packages/00/e7/4be35bc2adbebffb5ac7ede2b8459432194a82bd8f325af12b77b7c26248/pyobjc_framework_NetFS-11.0-py2.py3-none-any.whl", hash = "sha256:11e06da73a1d590b8462f3a1412604758d49b5e04d134b6e991282453b76abb8", size = 4088, upload-time = "2025-01-14T18:56:37.646Z" }, + { url = "https://files.pythonhosted.org/packages/fe/83/b7c8dfaee82c0312af25c2b31621505ce19f01fab7bb55eec69c0b4d24ad/pyobjc_framework_NetFS-11.0-py3-none-any.whl", hash = "sha256:9b69a36e3a6782ce37cd3140c584dd7d5c96f7355662d004a2927583b112b4dd", size = 4162, upload-time = "2025-01-14T18:56:38.682Z" }, ] [[package]] @@ -3543,10 +3543,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160, upload_time = "2025-01-14T19:04:50.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160, upload-time = "2025-01-14T19:04:50.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/b5/16800524e6d8d99467f53dbafa661abb1405d08d50def7edb933504197a3/pyobjc_framework_Network-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6fc797537690a241b555475923bcee28824efacd501e235457daeb4496b4b700", size = 19507, upload_time = "2025-01-14T18:56:41.544Z" }, - { url = "https://files.pythonhosted.org/packages/36/7c/a5966976564e8e71c0e66bf68e9282c279ad0c3ce81be61fa20ca8e0ca2e/pyobjc_framework_Network-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b9bb4a0cbd01cc4acb120ce313662763bca0c5ef11c01a0a0cae64c80b120c5", size = 19532, upload_time = "2025-01-14T18:56:42.499Z" }, + { url = "https://files.pythonhosted.org/packages/24/b5/16800524e6d8d99467f53dbafa661abb1405d08d50def7edb933504197a3/pyobjc_framework_Network-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6fc797537690a241b555475923bcee28824efacd501e235457daeb4496b4b700", size = 19507, upload-time = "2025-01-14T18:56:41.544Z" }, + { url = "https://files.pythonhosted.org/packages/36/7c/a5966976564e8e71c0e66bf68e9282c279ad0c3ce81be61fa20ca8e0ca2e/pyobjc_framework_Network-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b9bb4a0cbd01cc4acb120ce313662763bca0c5ef11c01a0a0cae64c80b120c5", size = 19532, upload-time = "2025-01-14T18:56:42.499Z" }, ] [[package]] @@ -3557,10 +3557,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551, upload_time = "2025-01-14T19:04:51.352Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551, upload-time = "2025-01-14T19:04:51.352Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/a4/120aba6e1ccf473d7294c200687f500b096947fec58d94dc772b1a444ecc/pyobjc_framework_NetworkExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bba4f338748c8ad2cb4320c4dd64b64772a863c6b6f991c2636b2a2f4cb839a", size = 13945, upload_time = "2025-01-14T18:56:53.477Z" }, - { url = "https://files.pythonhosted.org/packages/d1/0f/f7039d2bae0dcd63f66aff008613860499b6014dbd272726026f6c4c768d/pyobjc_framework_NetworkExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abf63433992ff1830f42cb813d1575473f0034ca6f62827f43bb2b33cc31e095", size = 13960, upload_time = "2025-01-14T18:56:55.661Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a4/120aba6e1ccf473d7294c200687f500b096947fec58d94dc772b1a444ecc/pyobjc_framework_NetworkExtension-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bba4f338748c8ad2cb4320c4dd64b64772a863c6b6f991c2636b2a2f4cb839a", size = 13945, upload-time = "2025-01-14T18:56:53.477Z" }, + { url = "https://files.pythonhosted.org/packages/d1/0f/f7039d2bae0dcd63f66aff008613860499b6014dbd272726026f6c4c768d/pyobjc_framework_NetworkExtension-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:abf63433992ff1830f42cb813d1575473f0034ca6f62827f43bb2b33cc31e095", size = 13960, upload-time = "2025-01-14T18:56:55.661Z" }, ] [[package]] @@ -3571,10 +3571,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844, upload_time = "2025-01-14T19:04:52.459Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844, upload-time = "2025-01-14T19:04:52.459Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/f2/22f04062b772e2f47ee2d54eac3f80c5aef727ec468ef5ab9a3272dd2a73/pyobjc_framework_NotificationCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:075853f3e36eb4377182589e552226b2207a575035d7e128055cfde9dcad84b7", size = 9684, upload_time = "2025-01-14T18:57:02.779Z" }, - { url = "https://files.pythonhosted.org/packages/16/22/531c2aab1639ab13aeaf3ac324afa102515b8d5eb860cb1a566018d98058/pyobjc_framework_NotificationCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:093e50badfbc78edf088f9241cddba7516a58188d401f299e361f1ec85e93fce", size = 9707, upload_time = "2025-01-14T18:57:03.659Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f2/22f04062b772e2f47ee2d54eac3f80c5aef727ec468ef5ab9a3272dd2a73/pyobjc_framework_NotificationCenter-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:075853f3e36eb4377182589e552226b2207a575035d7e128055cfde9dcad84b7", size = 9684, upload-time = "2025-01-14T18:57:02.779Z" }, + { url = "https://files.pythonhosted.org/packages/16/22/531c2aab1639ab13aeaf3ac324afa102515b8d5eb860cb1a566018d98058/pyobjc_framework_NotificationCenter-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:093e50badfbc78edf088f9241cddba7516a58188d401f299e361f1ec85e93fce", size = 9707, upload-time = "2025-01-14T18:57:03.659Z" }, ] [[package]] @@ -3585,10 +3585,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165, upload_time = "2025-01-14T19:04:53.42Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165, upload-time = "2025-01-14T19:04:53.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/0a/e5a03c46a5873db83fb89ea829e4a0c02fb3f56f3639a6053e72854f435b/pyobjc_framework_OpenDirectory-11.0-py2.py3-none-any.whl", hash = "sha256:8a0feeda5a7f34b25b72c71cd1e4dd57b636cc4103248ff91bcb8571d4915eb4", size = 11747, upload_time = "2025-01-14T18:57:17.445Z" }, - { url = "https://files.pythonhosted.org/packages/da/fd/be3815a19978ab2a3abe9563a031195b40647077fcebbee86232af260176/pyobjc_framework_OpenDirectory-11.0-py3-none-any.whl", hash = "sha256:bfac495de433a62e3934619e2f5d2254177f960b7d4e905ed4ef359127e23b24", size = 11816, upload_time = "2025-01-14T18:57:18.486Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0a/e5a03c46a5873db83fb89ea829e4a0c02fb3f56f3639a6053e72854f435b/pyobjc_framework_OpenDirectory-11.0-py2.py3-none-any.whl", hash = "sha256:8a0feeda5a7f34b25b72c71cd1e4dd57b636cc4103248ff91bcb8571d4915eb4", size = 11747, upload-time = "2025-01-14T18:57:17.445Z" }, + { url = "https://files.pythonhosted.org/packages/da/fd/be3815a19978ab2a3abe9563a031195b40647077fcebbee86232af260176/pyobjc_framework_OpenDirectory-11.0-py3-none-any.whl", hash = "sha256:bfac495de433a62e3934619e2f5d2254177f960b7d4e905ed4ef359127e23b24", size = 11816, upload-time = "2025-01-14T18:57:18.486Z" }, ] [[package]] @@ -3599,10 +3599,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535, upload_time = "2025-01-14T19:04:54.753Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535, upload-time = "2025-01-14T19:04:54.753Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/f6/1dcff2f76280946368ee75ab39c92e261a851656c5979a50513563d08cf0/pyobjc_framework_OSAKit-11.0-py2.py3-none-any.whl", hash = "sha256:3183414e345af83a2187b00356130909a7c2a41b2227dc579b662737300c3ba4", size = 4094, upload_time = "2025-01-14T18:57:08.639Z" }, - { url = "https://files.pythonhosted.org/packages/17/75/745985429f0ff4776ffb8ba261199e11f4d6977b1814ad2b39084f83bad5/pyobjc_framework_OSAKit-11.0-py3-none-any.whl", hash = "sha256:79150c47d2aeffc72fb6551060518ce472275edbad3b56aef5923a6086371c28", size = 4162, upload_time = "2025-01-14T18:57:09.71Z" }, + { url = "https://files.pythonhosted.org/packages/56/f6/1dcff2f76280946368ee75ab39c92e261a851656c5979a50513563d08cf0/pyobjc_framework_OSAKit-11.0-py2.py3-none-any.whl", hash = "sha256:3183414e345af83a2187b00356130909a7c2a41b2227dc579b662737300c3ba4", size = 4094, upload-time = "2025-01-14T18:57:08.639Z" }, + { url = "https://files.pythonhosted.org/packages/17/75/745985429f0ff4776ffb8ba261199e11f4d6977b1814ad2b39084f83bad5/pyobjc_framework_OSAKit-11.0-py3-none-any.whl", hash = "sha256:79150c47d2aeffc72fb6551060518ce472275edbad3b56aef5923a6086371c28", size = 4162, upload-time = "2025-01-14T18:57:09.71Z" }, ] [[package]] @@ -3615,10 +3615,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151, upload_time = "2025-01-14T19:04:55.587Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151, upload-time = "2025-01-14T19:04:55.587Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/54/6b507a18d0adadf8b707be9616bc9bab157963b81fa3c9928a0148d3bfd8/pyobjc_framework_OSLog-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0131851fca9b741f290ffa727dd30328dd8526b87c8cef623b79239bed99187", size = 7694, upload_time = "2025-01-14T18:57:12.713Z" }, - { url = "https://files.pythonhosted.org/packages/d1/79/81e64a55023f458aa5d99d10671fd9bcc6c0dcf8339768152fbc28c92cef/pyobjc_framework_OSLog-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:17d8b49113a476372b24ac8e544d88f6d12f878f1081dd611ab203c4484f2039", size = 7720, upload_time = "2025-01-14T18:57:13.695Z" }, + { url = "https://files.pythonhosted.org/packages/9c/54/6b507a18d0adadf8b707be9616bc9bab157963b81fa3c9928a0148d3bfd8/pyobjc_framework_OSLog-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0131851fca9b741f290ffa727dd30328dd8526b87c8cef623b79239bed99187", size = 7694, upload-time = "2025-01-14T18:57:12.713Z" }, + { url = "https://files.pythonhosted.org/packages/d1/79/81e64a55023f458aa5d99d10671fd9bcc6c0dcf8339768152fbc28c92cef/pyobjc_framework_OSLog-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:17d8b49113a476372b24ac8e544d88f6d12f878f1081dd611ab203c4484f2039", size = 7720, upload-time = "2025-01-14T18:57:13.695Z" }, ] [[package]] @@ -3629,10 +3629,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495, upload_time = "2025-01-14T19:04:57.689Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495, upload-time = "2025-01-14T19:04:57.689Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/72/d7dae8f5a1c5b12d9cf404a71a82fd5a638bc4de2d1099bf838aee1026f0/pyobjc_framework_PassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:710372134c3adedb9017bfc2fbc592ef0e94ae916145b58e57234239bf903b90", size = 14354, upload_time = "2025-01-14T18:57:24.436Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b1/5ee2f5581877241a4fc2db4ab4a33d595a918bde1b4a59796240e2b2244b/pyobjc_framework_PassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe0144177f7feb96577bea53841d9b9b3f63185735a1bf1b36368ab189fd6282", size = 14391, upload_time = "2025-01-14T18:57:26.67Z" }, + { url = "https://files.pythonhosted.org/packages/53/72/d7dae8f5a1c5b12d9cf404a71a82fd5a638bc4de2d1099bf838aee1026f0/pyobjc_framework_PassKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:710372134c3adedb9017bfc2fbc592ef0e94ae916145b58e57234239bf903b90", size = 14354, upload-time = "2025-01-14T18:57:24.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b1/5ee2f5581877241a4fc2db4ab4a33d595a918bde1b4a59796240e2b2244b/pyobjc_framework_PassKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe0144177f7feb96577bea53841d9b9b3f63185735a1bf1b36368ab189fd6282", size = 14391, upload-time = "2025-01-14T18:57:26.67Z" }, ] [[package]] @@ -3643,10 +3643,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812, upload_time = "2025-01-14T19:04:58.652Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812, upload-time = "2025-01-14T19:04:58.652Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/5b/24fb83a97648eaa0d231df7908532dff7b36d5f516d55c92ed9ae07c4e1b/pyobjc_framework_PencilKit-11.0-py2.py3-none-any.whl", hash = "sha256:22cbb6ed2504be4c8d631c4711b00fae48ef731c10c69861b4de1e4fcdc19279", size = 3970, upload_time = "2025-01-14T18:57:30.597Z" }, - { url = "https://files.pythonhosted.org/packages/08/fd/89a005c86b06137837952838d976ce6e39b31082392d78c382d44e03944d/pyobjc_framework_PencilKit-11.0-py3-none-any.whl", hash = "sha256:a4e606c5b69e6adb80ef30fc95fe0095971735d12ab6fc4fe4d982e4c8a3881a", size = 4045, upload_time = "2025-01-14T18:57:31.87Z" }, + { url = "https://files.pythonhosted.org/packages/af/5b/24fb83a97648eaa0d231df7908532dff7b36d5f516d55c92ed9ae07c4e1b/pyobjc_framework_PencilKit-11.0-py2.py3-none-any.whl", hash = "sha256:22cbb6ed2504be4c8d631c4711b00fae48ef731c10c69861b4de1e4fcdc19279", size = 3970, upload-time = "2025-01-14T18:57:30.597Z" }, + { url = "https://files.pythonhosted.org/packages/08/fd/89a005c86b06137837952838d976ce6e39b31082392d78c382d44e03944d/pyobjc_framework_PencilKit-11.0-py3-none-any.whl", hash = "sha256:a4e606c5b69e6adb80ef30fc95fe0095971735d12ab6fc4fe4d982e4c8a3881a", size = 4045, upload-time = "2025-01-14T18:57:31.87Z" }, ] [[package]] @@ -3657,10 +3657,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-avfoundation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214, upload_time = "2025-01-14T19:04:59.461Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214, upload-time = "2025-01-14T19:04:59.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/97/efb9d770ba05d285384b0c121e9e911929893356da1944a0bb03ea0df0f2/pyobjc_framework_PHASE-11.0-py2.py3-none-any.whl", hash = "sha256:d3e41c2b2fdf4b2ce39f558a08762c6864449ff87b618e42747777ad3f821323", size = 6777, upload_time = "2025-01-14T18:57:20.135Z" }, - { url = "https://files.pythonhosted.org/packages/38/85/03420927e4243d0ef8e3e8aa1ca511b5638743d7ec319a570a472a50d60f/pyobjc_framework_PHASE-11.0-py3-none-any.whl", hash = "sha256:78c0600477ea294304b51f8284a2fb299be284c33ae2c135e1c7cd26fdf4def4", size = 6846, upload_time = "2025-01-14T18:57:21.193Z" }, + { url = "https://files.pythonhosted.org/packages/a9/97/efb9d770ba05d285384b0c121e9e911929893356da1944a0bb03ea0df0f2/pyobjc_framework_PHASE-11.0-py2.py3-none-any.whl", hash = "sha256:d3e41c2b2fdf4b2ce39f558a08762c6864449ff87b618e42747777ad3f821323", size = 6777, upload-time = "2025-01-14T18:57:20.135Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/03420927e4243d0ef8e3e8aa1ca511b5638743d7ec319a570a472a50d60f/pyobjc_framework_PHASE-11.0-py3-none-any.whl", hash = "sha256:78c0600477ea294304b51f8284a2fb299be284c33ae2c135e1c7cd26fdf4def4", size = 6846, upload-time = "2025-01-14T18:57:21.193Z" }, ] [[package]] @@ -3671,10 +3671,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122, upload_time = "2025-01-14T19:05:01.804Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122, upload-time = "2025-01-14T19:05:01.804Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/27/62e5833b9629121b4b6ea8f2b2aa295cf6b719dc6316387f77ec0bd41d77/pyobjc_framework_Photos-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:71bf849888713e4a00eb44074c5000ed081c905ba35b3a55ee84c6367ce60ce8", size = 12085, upload_time = "2025-01-14T18:57:34.615Z" }, - { url = "https://files.pythonhosted.org/packages/b9/6e/54108271ea34b0fc51bf8d0bf677788e4d39a1e29ad481f8c78c100f3159/pyobjc_framework_Photos-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ea630c3abf4620b022f23167ef5f3d6b157b38697d7ffc5df0fc507e95bed655", size = 12107, upload_time = "2025-01-14T18:57:35.66Z" }, + { url = "https://files.pythonhosted.org/packages/80/27/62e5833b9629121b4b6ea8f2b2aa295cf6b719dc6316387f77ec0bd41d77/pyobjc_framework_Photos-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:71bf849888713e4a00eb44074c5000ed081c905ba35b3a55ee84c6367ce60ce8", size = 12085, upload-time = "2025-01-14T18:57:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/b9/6e/54108271ea34b0fc51bf8d0bf677788e4d39a1e29ad481f8c78c100f3159/pyobjc_framework_Photos-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ea630c3abf4620b022f23167ef5f3d6b157b38697d7ffc5df0fc507e95bed655", size = 12107, upload-time = "2025-01-14T18:57:35.66Z" }, ] [[package]] @@ -3685,10 +3685,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898, upload_time = "2025-01-14T19:05:02.737Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898, upload-time = "2025-01-14T19:05:02.737Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/ec/9574692e2852d546b28bac853b2b0584c4d4f093a4befac0e105789ee9f6/pyobjc_framework_PhotosUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b3865d2cc4fad4d34255941fe93ce504b9d2c7a7043bd0f4c715da9f4af1cf1", size = 12165, upload_time = "2025-01-14T18:57:44.048Z" }, - { url = "https://files.pythonhosted.org/packages/90/a9/85d70fe9eee0d15a0615a3f7b2ef92120c32614e350286d347d733fcf1d0/pyobjc_framework_PhotosUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66826184121cd15415750d801160721adad80b53cbb315192522229b17252ebb", size = 12176, upload_time = "2025-01-14T18:57:44.993Z" }, + { url = "https://files.pythonhosted.org/packages/94/ec/9574692e2852d546b28bac853b2b0584c4d4f093a4befac0e105789ee9f6/pyobjc_framework_PhotosUI-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b3865d2cc4fad4d34255941fe93ce504b9d2c7a7043bd0f4c715da9f4af1cf1", size = 12165, upload-time = "2025-01-14T18:57:44.048Z" }, + { url = "https://files.pythonhosted.org/packages/90/a9/85d70fe9eee0d15a0615a3f7b2ef92120c32614e350286d347d733fcf1d0/pyobjc_framework_PhotosUI-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:66826184121cd15415750d801160721adad80b53cbb315192522229b17252ebb", size = 12176, upload-time = "2025-01-14T18:57:44.993Z" }, ] [[package]] @@ -3699,10 +3699,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419, upload_time = "2025-01-14T19:05:03.787Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419, upload-time = "2025-01-14T19:05:03.787Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/f7/5d0d9b94563ef06fe0a9c15ba2b77922b73bcc4b6630c487936edf382e20/pyobjc_framework_PreferencePanes-11.0-py2.py3-none-any.whl", hash = "sha256:2143851549430d6bb951adae44cb65c1986662ac7c8cbe15891ed194cbe283a2", size = 4706, upload_time = "2025-01-14T18:57:51.425Z" }, - { url = "https://files.pythonhosted.org/packages/9b/0e/76d694eea953b39318249ae24c956c3e115d8222343fb01f0186f7ca0043/pyobjc_framework_PreferencePanes-11.0-py3-none-any.whl", hash = "sha256:9f1287716374338fa99445ca13dfcc6c9be5597c8a5ce06680a8ca245b4e0acc", size = 4772, upload_time = "2025-01-14T18:57:52.684Z" }, + { url = "https://files.pythonhosted.org/packages/70/f7/5d0d9b94563ef06fe0a9c15ba2b77922b73bcc4b6630c487936edf382e20/pyobjc_framework_PreferencePanes-11.0-py2.py3-none-any.whl", hash = "sha256:2143851549430d6bb951adae44cb65c1986662ac7c8cbe15891ed194cbe283a2", size = 4706, upload-time = "2025-01-14T18:57:51.425Z" }, + { url = "https://files.pythonhosted.org/packages/9b/0e/76d694eea953b39318249ae24c956c3e115d8222343fb01f0186f7ca0043/pyobjc_framework_PreferencePanes-11.0-py3-none-any.whl", hash = "sha256:9f1287716374338fa99445ca13dfcc6c9be5597c8a5ce06680a8ca245b4e0acc", size = 4772, upload-time = "2025-01-14T18:57:52.684Z" }, ] [[package]] @@ -3713,10 +3713,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816, upload_time = "2025-01-14T19:05:05.468Z" } +sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816, upload-time = "2025-01-14T19:05:05.468Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/5f/de178da22fa628cd88f599fea2a70b7d1d9ebc65576307df0bf29822a347/pyobjc_framework_PushKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0185cebcc5aad73aae50804c7a2412da6275717b8f872b830d71c484efcdea7a", size = 8010, upload_time = "2025-01-14T18:57:59.042Z" }, - { url = "https://files.pythonhosted.org/packages/5f/a5/60f93031302aba7cdff28728b8141b58c3bd5c12f4a6cef5796a8cc2e666/pyobjc_framework_PushKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:43bd1ed31664982e4d8397a7e07e58a7deb85bf9c9866ea966fd7ca25796014c", size = 8032, upload_time = "2025-01-14T18:57:59.973Z" }, + { url = "https://files.pythonhosted.org/packages/17/5f/de178da22fa628cd88f599fea2a70b7d1d9ebc65576307df0bf29822a347/pyobjc_framework_PushKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0185cebcc5aad73aae50804c7a2412da6275717b8f872b830d71c484efcdea7a", size = 8010, upload-time = "2025-01-14T18:57:59.042Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a5/60f93031302aba7cdff28728b8141b58c3bd5c12f4a6cef5796a8cc2e666/pyobjc_framework_PushKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:43bd1ed31664982e4d8397a7e07e58a7deb85bf9c9866ea966fd7ca25796014c", size = 8032, upload-time = "2025-01-14T18:57:59.973Z" }, ] [[package]] @@ -3727,10 +3727,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463, upload_time = "2025-01-14T19:05:07.931Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463, upload-time = "2025-01-14T19:05:07.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/6a/68957c8c5e8f0128d4d419728bac397d48fa7ad7a66e82b70e64d129ffca/pyobjc_framework_Quartz-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d251696bfd8e8ef72fbc90eb29fec95cb9d1cc409008a183d5cc3246130ae8c2", size = 212349, upload_time = "2025-01-14T18:58:08.963Z" }, - { url = "https://files.pythonhosted.org/packages/60/5d/df827b78dcb5140652ad08af8038c9ddd7e01e6bdf84462bfee644e6e661/pyobjc_framework_Quartz-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cb4a9f2d9d580ea15e25e6b270f47681afb5689cafc9e25712445ce715bcd18e", size = 212061, upload_time = "2025-01-14T18:58:10.2Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/68957c8c5e8f0128d4d419728bac397d48fa7ad7a66e82b70e64d129ffca/pyobjc_framework_Quartz-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d251696bfd8e8ef72fbc90eb29fec95cb9d1cc409008a183d5cc3246130ae8c2", size = 212349, upload-time = "2025-01-14T18:58:08.963Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/df827b78dcb5140652ad08af8038c9ddd7e01e6bdf84462bfee644e6e661/pyobjc_framework_Quartz-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cb4a9f2d9d580ea15e25e6b270f47681afb5689cafc9e25712445ce715bcd18e", size = 212061, upload-time = "2025-01-14T18:58:10.2Z" }, ] [[package]] @@ -3742,10 +3742,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784, upload_time = "2025-01-14T19:05:09.857Z" } +sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784, upload-time = "2025-01-14T19:05:09.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/85/1a66fefa99e7a4eb7534b2f56f9a24d33beda450dd2ca45d180307e76c74/pyobjc_framework_QuickLookThumbnailing-11.0-py2.py3-none-any.whl", hash = "sha256:6e567a764942845ce4db7ccfc0f8a9d091216bd029ecca955e618a43d64a5d84", size = 4164, upload_time = "2025-01-14T18:58:16.381Z" }, - { url = "https://files.pythonhosted.org/packages/05/d7/26decb13136b7c95a1ca3ecf202644ad2fd515a57e1117c71bfc86429b20/pyobjc_framework_QuickLookThumbnailing-11.0-py3-none-any.whl", hash = "sha256:e0f7f62b9a1df55e5f717518baf3260dc2cb8a9722cc5e9c6fffc643f69bda27", size = 4229, upload_time = "2025-01-14T18:58:17.404Z" }, + { url = "https://files.pythonhosted.org/packages/9d/85/1a66fefa99e7a4eb7534b2f56f9a24d33beda450dd2ca45d180307e76c74/pyobjc_framework_QuickLookThumbnailing-11.0-py2.py3-none-any.whl", hash = "sha256:6e567a764942845ce4db7ccfc0f8a9d091216bd029ecca955e618a43d64a5d84", size = 4164, upload-time = "2025-01-14T18:58:16.381Z" }, + { url = "https://files.pythonhosted.org/packages/05/d7/26decb13136b7c95a1ca3ecf202644ad2fd515a57e1117c71bfc86429b20/pyobjc_framework_QuickLookThumbnailing-11.0-py3-none-any.whl", hash = "sha256:e0f7f62b9a1df55e5f717518baf3260dc2cb8a9722cc5e9c6fffc643f69bda27", size = 4229, upload-time = "2025-01-14T18:58:17.404Z" }, ] [[package]] @@ -3756,10 +3756,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589, upload_time = "2025-01-14T19:05:10.791Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589, upload-time = "2025-01-14T19:05:10.791Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/56/89a8544426a46bf176c9462511c08d4c94ae7e0403abb2d73632af68ee8e/pyobjc_framework_ReplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:262fb834400e8379f4c795e65137763348992f3010284602d876050b8adb9ea4", size = 9904, upload_time = "2025-01-14T18:58:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/47/af/9abfa41060efc96000cc9ae77f302bb8210f3be0f793ba5d11f98a03e468/pyobjc_framework_ReplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9db123ee52761a670c6e41e5f9d9a47a2ca5582a9c4a7c8662a8bb56a0f593", size = 9903, upload_time = "2025-01-14T18:58:20.222Z" }, + { url = "https://files.pythonhosted.org/packages/fe/56/89a8544426a46bf176c9462511c08d4c94ae7e0403abb2d73632af68ee8e/pyobjc_framework_ReplayKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:262fb834400e8379f4c795e65137763348992f3010284602d876050b8adb9ea4", size = 9904, upload-time = "2025-01-14T18:58:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/47/af/9abfa41060efc96000cc9ae77f302bb8210f3be0f793ba5d11f98a03e468/pyobjc_framework_ReplayKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:da9db123ee52761a670c6e41e5f9d9a47a2ca5582a9c4a7c8662a8bb56a0f593", size = 9903, upload-time = "2025-01-14T18:58:20.222Z" }, ] [[package]] @@ -3770,10 +3770,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367, upload_time = "2025-01-14T19:05:12.914Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367, upload-time = "2025-01-14T19:05:12.914Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/39/d69f8e7dbf6f366cb5fdaa8aa7ceef1dadb93a5e4d9fc63217477bba5e32/pyobjc_framework_SafariServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55c02a533073e0a2aaf6db544f087fd861bace6b62035c3bb2e6b20f0b921b2b", size = 7262, upload_time = "2025-01-14T18:58:29.754Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/a625330bdf7a5d9962299562b6e19f6cbd1ea1b14887958e42a4372d3344/pyobjc_framework_SafariServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31ba086a39ee06d8622a504e3ea3a1f6dc8fab1d4c4c7930d5af6e989f38ec56", size = 7262, upload_time = "2025-01-14T18:58:30.725Z" }, + { url = "https://files.pythonhosted.org/packages/40/39/d69f8e7dbf6f366cb5fdaa8aa7ceef1dadb93a5e4d9fc63217477bba5e32/pyobjc_framework_SafariServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:55c02a533073e0a2aaf6db544f087fd861bace6b62035c3bb2e6b20f0b921b2b", size = 7262, upload-time = "2025-01-14T18:58:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/36/76/a625330bdf7a5d9962299562b6e19f6cbd1ea1b14887958e42a4372d3344/pyobjc_framework_SafariServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31ba086a39ee06d8622a504e3ea3a1f6dc8fab1d4c4c7930d5af6e989f38ec56", size = 7262, upload-time = "2025-01-14T18:58:30.725Z" }, ] [[package]] @@ -3785,10 +3785,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745, upload_time = "2025-01-14T19:05:13.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745, upload-time = "2025-01-14T19:05:13.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/c5/68b79c0f128eb735397aa68a40e5ac48b88c12967f69358f25f753a3fc1c/pyobjc_framework_SafetyKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83a1f313c9c63ba107a7c543a8300ae225fa5ff17d963b1c499859da45ceaf55", size = 8395, upload_time = "2025-01-14T18:58:35.46Z" }, - { url = "https://files.pythonhosted.org/packages/99/02/2853a00e75cca8db8b5053ff2648ff2a26f5c02f07af1c70630a36b58d04/pyobjc_framework_SafetyKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c6dd23fcaca9c41d6aadf2ca0a6d07c4032a0c4ea8873ee06da6efd1e868f97e", size = 8418, upload_time = "2025-01-14T18:58:36.369Z" }, + { url = "https://files.pythonhosted.org/packages/37/c5/68b79c0f128eb735397aa68a40e5ac48b88c12967f69358f25f753a3fc1c/pyobjc_framework_SafetyKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:83a1f313c9c63ba107a7c543a8300ae225fa5ff17d963b1c499859da45ceaf55", size = 8395, upload-time = "2025-01-14T18:58:35.46Z" }, + { url = "https://files.pythonhosted.org/packages/99/02/2853a00e75cca8db8b5053ff2648ff2a26f5c02f07af1c70630a36b58d04/pyobjc_framework_SafetyKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c6dd23fcaca9c41d6aadf2ca0a6d07c4032a0c4ea8873ee06da6efd1e868f97e", size = 8418, upload-time = "2025-01-14T18:58:36.369Z" }, ] [[package]] @@ -3800,10 +3800,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647, upload_time = "2025-01-14T19:05:15.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647, upload-time = "2025-01-14T19:05:15.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/4c/5ec624ae043fbbe15be2a989e3fc6cb08d992e0a5061450b84b33f96429c/pyobjc_framework_SceneKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86d23456e4c7a7bb7bb49be2b98647678ac7a39955e6bb242e0ac125d8b770e8", size = 33108, upload_time = "2025-01-14T18:58:43.577Z" }, - { url = "https://files.pythonhosted.org/packages/b8/7f/fef1cf3eaf1366a6f3f93c5a6b164acfdfdc2d15b3243b70763ac217ce03/pyobjc_framework_SceneKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d0a0d557167adddf27a42fb109a1dce29a22ff09aca34558fccd1c22f08ae2b4", size = 33130, upload_time = "2025-01-14T18:58:44.549Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4c/5ec624ae043fbbe15be2a989e3fc6cb08d992e0a5061450b84b33f96429c/pyobjc_framework_SceneKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86d23456e4c7a7bb7bb49be2b98647678ac7a39955e6bb242e0ac125d8b770e8", size = 33108, upload-time = "2025-01-14T18:58:43.577Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7f/fef1cf3eaf1366a6f3f93c5a6b164acfdfdc2d15b3243b70763ac217ce03/pyobjc_framework_SceneKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d0a0d557167adddf27a42fb109a1dce29a22ff09aca34558fccd1c22f08ae2b4", size = 33130, upload-time = "2025-01-14T18:58:44.549Z" }, ] [[package]] @@ -3815,10 +3815,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coremedia" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046, upload_time = "2025-01-14T19:05:16.834Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046, upload-time = "2025-01-14T19:05:16.834Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/aa/d6d0818564570065411874cbe3de86dee105dc9906161c0584009a1a63bc/pyobjc_framework_ScreenCaptureKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38468e833ec1498778bd33ce30578afed2e13ac14c73e8e6290ff06a2e0c50d8", size = 11110, upload_time = "2025-01-14T18:58:51.475Z" }, - { url = "https://files.pythonhosted.org/packages/27/61/557e725aef9ad76a1a7c48b361f8c5636a606cbaf9ba520ff8f69d3cf791/pyobjc_framework_ScreenCaptureKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7d8a83dcc0950699242677cfefda545b9c0a0567111f8f3d3df1cf6ed75ea480", size = 11121, upload_time = "2025-01-14T18:58:53.055Z" }, + { url = "https://files.pythonhosted.org/packages/af/aa/d6d0818564570065411874cbe3de86dee105dc9906161c0584009a1a63bc/pyobjc_framework_ScreenCaptureKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:38468e833ec1498778bd33ce30578afed2e13ac14c73e8e6290ff06a2e0c50d8", size = 11110, upload-time = "2025-01-14T18:58:51.475Z" }, + { url = "https://files.pythonhosted.org/packages/27/61/557e725aef9ad76a1a7c48b361f8c5636a606cbaf9ba520ff8f69d3cf791/pyobjc_framework_ScreenCaptureKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7d8a83dcc0950699242677cfefda545b9c0a0567111f8f3d3df1cf6ed75ea480", size = 11121, upload-time = "2025-01-14T18:58:53.055Z" }, ] [[package]] @@ -3829,10 +3829,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774, upload_time = "2025-01-14T19:05:19.325Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774, upload-time = "2025-01-14T19:05:19.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/ab/f17cd36458e6cf6d64c412128641edcfc220b8147283f6b34ef56c7db111/pyobjc_framework_ScreenSaver-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:436357c822d87220df64912da04b421e82a5e1e6464d48f2dbccc69529d19cd3", size = 8445, upload_time = "2025-01-14T18:58:59.299Z" }, - { url = "https://files.pythonhosted.org/packages/52/57/300b641e929741a5d38cf80c74496918be1d2fe5e210d3fceb3e768747b2/pyobjc_framework_ScreenSaver-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:03b12e89bc164cb01527ca795f3f590f286d15de6ee0e4ff1d36705740d6d72f", size = 8372, upload_time = "2025-01-14T18:59:00.358Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ab/f17cd36458e6cf6d64c412128641edcfc220b8147283f6b34ef56c7db111/pyobjc_framework_ScreenSaver-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:436357c822d87220df64912da04b421e82a5e1e6464d48f2dbccc69529d19cd3", size = 8445, upload-time = "2025-01-14T18:58:59.299Z" }, + { url = "https://files.pythonhosted.org/packages/52/57/300b641e929741a5d38cf80c74496918be1d2fe5e210d3fceb3e768747b2/pyobjc_framework_ScreenSaver-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:03b12e89bc164cb01527ca795f3f590f286d15de6ee0e4ff1d36705740d6d72f", size = 8372, upload-time = "2025-01-14T18:59:00.358Z" }, ] [[package]] @@ -3843,10 +3843,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398, upload_time = "2025-01-14T19:05:21.547Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398, upload-time = "2025-01-14T19:05:21.547Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/7a/8df61f80725e993fd0dc1a111217de6a8efec35b02a4796749de0b7e8c34/pyobjc_framework_ScreenTime-11.0-py2.py3-none-any.whl", hash = "sha256:723938c7d47e3c5c1c0f79010a01139762384bd0c03c51ee7a4736fc3f128fed", size = 3721, upload_time = "2025-01-14T18:59:04.027Z" }, - { url = "https://files.pythonhosted.org/packages/c4/62/2f86cedd4cc439625976848832c1d1571fcb69cc087dd71c9cf09e793db5/pyobjc_framework_ScreenTime-11.0-py3-none-any.whl", hash = "sha256:45db846ec9249cab90e86cbb31cf70e13800305b7c74819ab681a91854c91df2", size = 3790, upload_time = "2025-01-14T18:59:06.363Z" }, + { url = "https://files.pythonhosted.org/packages/40/7a/8df61f80725e993fd0dc1a111217de6a8efec35b02a4796749de0b7e8c34/pyobjc_framework_ScreenTime-11.0-py2.py3-none-any.whl", hash = "sha256:723938c7d47e3c5c1c0f79010a01139762384bd0c03c51ee7a4736fc3f128fed", size = 3721, upload-time = "2025-01-14T18:59:04.027Z" }, + { url = "https://files.pythonhosted.org/packages/c4/62/2f86cedd4cc439625976848832c1d1571fcb69cc087dd71c9cf09e793db5/pyobjc_framework_ScreenTime-11.0-py3-none-any.whl", hash = "sha256:45db846ec9249cab90e86cbb31cf70e13800305b7c74819ab681a91854c91df2", size = 3790, upload-time = "2025-01-14T18:59:06.363Z" }, ] [[package]] @@ -3857,10 +3857,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626, upload_time = "2025-01-14T19:05:22.461Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626, upload-time = "2025-01-14T19:05:22.461Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/2c/2fd33c0318a8fe35f00f0089a44a2c27d4d0fd0b4b5e13628051a4d8c9d3/pyobjc_framework_ScriptingBridge-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c98d080446aa8ba4074e43eb0be1feed96781dbc0718496f172fcd20e84a9158", size = 8209, upload_time = "2025-01-14T18:59:11.107Z" }, - { url = "https://files.pythonhosted.org/packages/93/3b/b2b721248e951eef6b7e6b25cb3a1d6683702235bc73683d0239f068d2df/pyobjc_framework_ScriptingBridge-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:23a4b2e2e57b7b4d992777ea9efb15273ccd8e8105385143dab9bd5a10962317", size = 8238, upload_time = "2025-01-14T18:59:13.27Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2c/2fd33c0318a8fe35f00f0089a44a2c27d4d0fd0b4b5e13628051a4d8c9d3/pyobjc_framework_ScriptingBridge-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c98d080446aa8ba4074e43eb0be1feed96781dbc0718496f172fcd20e84a9158", size = 8209, upload-time = "2025-01-14T18:59:11.107Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/b2b721248e951eef6b7e6b25cb3a1d6683702235bc73683d0239f068d2df/pyobjc_framework_ScriptingBridge-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:23a4b2e2e57b7b4d992777ea9efb15273ccd8e8105385143dab9bd5a10962317", size = 8238, upload-time = "2025-01-14T18:59:13.27Z" }, ] [[package]] @@ -3871,10 +3871,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-coreservices" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182, upload_time = "2025-01-14T19:05:24.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182, upload-time = "2025-01-14T19:05:24.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/d4/64fa608b5d91859b11c26ceca83a41d2bf1d0dcbf1d9df847bab5a52ccc8/pyobjc_framework_SearchKit-11.0-py2.py3-none-any.whl", hash = "sha256:332f9d30ec3b223efaac681fbdd923ba660575e241abb4ed5e03207c97799530", size = 3633, upload_time = "2025-01-14T18:59:18.343Z" }, - { url = "https://files.pythonhosted.org/packages/93/e2/83e94c505c5436821982d724cc890f74d717f9473782f7278ce78634685d/pyobjc_framework_SearchKit-11.0-py3-none-any.whl", hash = "sha256:5f4304cb77c327b28ac0f7ec9b99313075afd742091d39368eb64f076bb7d141", size = 3699, upload_time = "2025-01-14T18:59:20.754Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d4/64fa608b5d91859b11c26ceca83a41d2bf1d0dcbf1d9df847bab5a52ccc8/pyobjc_framework_SearchKit-11.0-py2.py3-none-any.whl", hash = "sha256:332f9d30ec3b223efaac681fbdd923ba660575e241abb4ed5e03207c97799530", size = 3633, upload-time = "2025-01-14T18:59:18.343Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/83e94c505c5436821982d724cc890f74d717f9473782f7278ce78634685d/pyobjc_framework_SearchKit-11.0-py3-none-any.whl", hash = "sha256:5f4304cb77c327b28ac0f7ec9b99313075afd742091d39368eb64f076bb7d141", size = 3699, upload-time = "2025-01-14T18:59:20.754Z" }, ] [[package]] @@ -3885,10 +3885,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059, upload_time = "2025-01-14T19:05:26.17Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059, upload-time = "2025-01-14T19:05:26.17Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/d8/092940f8c46cf09000a9d026e9854772846d5335e3e8a44d0a81aa1f359e/pyobjc_framework_Security-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:93bc23630563de2551ac49048af010ac9cb40f927cc25c898b7cc48550ccd526", size = 41499, upload_time = "2025-01-14T18:59:22.819Z" }, - { url = "https://files.pythonhosted.org/packages/0b/fc/8710bbe80b825c97ecc312aaead3b0f606a23b62b895f6e0a07df8bfeeae/pyobjc_framework_Security-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:421e03b8560ed296a7f5ee67f42f5f978f8c7959d65c8fec99cd77dc65786355", size = 41523, upload_time = "2025-01-14T18:59:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d8/092940f8c46cf09000a9d026e9854772846d5335e3e8a44d0a81aa1f359e/pyobjc_framework_Security-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:93bc23630563de2551ac49048af010ac9cb40f927cc25c898b7cc48550ccd526", size = 41499, upload-time = "2025-01-14T18:59:22.819Z" }, + { url = "https://files.pythonhosted.org/packages/0b/fc/8710bbe80b825c97ecc312aaead3b0f606a23b62b895f6e0a07df8bfeeae/pyobjc_framework_Security-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:421e03b8560ed296a7f5ee67f42f5f978f8c7959d65c8fec99cd77dc65786355", size = 41523, upload-time = "2025-01-14T18:59:25.368Z" }, ] [[package]] @@ -3900,10 +3900,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-security" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526, upload_time = "2025-01-14T19:05:27.275Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526, upload-time = "2025-01-14T19:05:27.275Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/41/50da30e87841c8b9ee1f17e9720dc9dbb2c2e59abac84fffe899ed5f9188/pyobjc_framework_SecurityFoundation-11.0-py2.py3-none-any.whl", hash = "sha256:8f8e43b91ae7cb45f3251c14c0c6caf5fdcdb93794176c4b118214a108ee2ef3", size = 3716, upload_time = "2025-01-14T18:59:29.79Z" }, - { url = "https://files.pythonhosted.org/packages/cb/61/e73a61de62e31b33378ee635534228f4801b1554fbd89a47e0b36965908d/pyobjc_framework_SecurityFoundation-11.0-py3-none-any.whl", hash = "sha256:1fa89969fbf7a4fd57214388a43f7ed6b6b1fd0c0ec7aa77752444eb1604143c", size = 3787, upload_time = "2025-01-14T18:59:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/f8/41/50da30e87841c8b9ee1f17e9720dc9dbb2c2e59abac84fffe899ed5f9188/pyobjc_framework_SecurityFoundation-11.0-py2.py3-none-any.whl", hash = "sha256:8f8e43b91ae7cb45f3251c14c0c6caf5fdcdb93794176c4b118214a108ee2ef3", size = 3716, upload-time = "2025-01-14T18:59:29.79Z" }, + { url = "https://files.pythonhosted.org/packages/cb/61/e73a61de62e31b33378ee635534228f4801b1554fbd89a47e0b36965908d/pyobjc_framework_SecurityFoundation-11.0-py3-none-any.whl", hash = "sha256:1fa89969fbf7a4fd57214388a43f7ed6b6b1fd0c0ec7aa77752444eb1604143c", size = 3787, upload-time = "2025-01-14T18:59:30.764Z" }, ] [[package]] @@ -3915,10 +3915,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-security" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118, upload_time = "2025-01-14T19:05:28.569Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118, upload-time = "2025-01-14T19:05:28.569Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/a96da5f43da5a9d0e5d016bc672a4dca09f88d091c96d9ecff5f753ad1d5/pyobjc_framework_SecurityInterface-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2771dae043c8aa278887f96c7d206957164c7a81a562fa391bf0b9316d6755eb", size = 10706, upload_time = "2025-01-14T18:59:32.632Z" }, - { url = "https://files.pythonhosted.org/packages/50/86/fc41dcf8f5300ad2c6508568535d9c0a83b412b0a4a961616441c8acf10f/pyobjc_framework_SecurityInterface-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6453732f7608d514e8f7005d80d238422cbebc4ab4d6d6fed1e51175f9f7244f", size = 10781, upload_time = "2025-01-14T18:59:33.832Z" }, + { url = "https://files.pythonhosted.org/packages/88/5f/a96da5f43da5a9d0e5d016bc672a4dca09f88d091c96d9ecff5f753ad1d5/pyobjc_framework_SecurityInterface-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2771dae043c8aa278887f96c7d206957164c7a81a562fa391bf0b9316d6755eb", size = 10706, upload-time = "2025-01-14T18:59:32.632Z" }, + { url = "https://files.pythonhosted.org/packages/50/86/fc41dcf8f5300ad2c6508568535d9c0a83b412b0a4a961616441c8acf10f/pyobjc_framework_SecurityInterface-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6453732f7608d514e8f7005d80d238422cbebc4ab4d6d6fed1e51175f9f7244f", size = 10781, upload-time = "2025-01-14T18:59:33.832Z" }, ] [[package]] @@ -3930,10 +3930,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060, upload_time = "2025-01-14T19:05:29.655Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060, upload-time = "2025-01-14T19:05:29.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/eb/e0d60b3e233860a237fdddd44ab961c9115c33e947058d73c222dafc50af/pyobjc_framework_SensitiveContentAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:e19d2edc807f98aef31fa4db5472a509cf90523436c971d1095a000b0e357058", size = 3791, upload_time = "2025-01-14T18:59:39.563Z" }, - { url = "https://files.pythonhosted.org/packages/c4/1c/fb2138cf08cd0215ea4f78032871a1d89e7e41d9fad18b55e937f0577c03/pyobjc_framework_SensitiveContentAnalysis-11.0-py3-none-any.whl", hash = "sha256:027bd0be0785f7aea3bfd56ff7c3496e5d383211122393c599c28ea392675589", size = 3863, upload_time = "2025-01-14T18:59:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/3d/eb/e0d60b3e233860a237fdddd44ab961c9115c33e947058d73c222dafc50af/pyobjc_framework_SensitiveContentAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:e19d2edc807f98aef31fa4db5472a509cf90523436c971d1095a000b0e357058", size = 3791, upload-time = "2025-01-14T18:59:39.563Z" }, + { url = "https://files.pythonhosted.org/packages/c4/1c/fb2138cf08cd0215ea4f78032871a1d89e7e41d9fad18b55e937f0577c03/pyobjc_framework_SensitiveContentAnalysis-11.0-py3-none-any.whl", hash = "sha256:027bd0be0785f7aea3bfd56ff7c3496e5d383211122393c599c28ea392675589", size = 3863, upload-time = "2025-01-14T18:59:40.548Z" }, ] [[package]] @@ -3944,10 +3944,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882, upload_time = "2025-01-14T19:05:30.537Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882, upload-time = "2025-01-14T19:05:30.537Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/35/cbac7db272d0e5e71b300be1517b0a1dc7cf035944675eaed7066d41e883/pyobjc_framework_ServiceManagement-11.0-py2.py3-none-any.whl", hash = "sha256:35cfd7a369a120fa55e64b719a2dda00295b2cc6ddab16ffa8939f4326d1b37d", size = 5254, upload_time = "2025-01-14T18:59:41.438Z" }, - { url = "https://files.pythonhosted.org/packages/b3/40/26c5d63d131e3e415815bfbb4bd035ba10d45f0d87733646221966871b6b/pyobjc_framework_ServiceManagement-11.0-py3-none-any.whl", hash = "sha256:7ec19c9632f67d589ad37815d001e8e443d92e75001c370486a1070a4359e166", size = 5322, upload_time = "2025-01-14T18:59:42.585Z" }, + { url = "https://files.pythonhosted.org/packages/5b/35/cbac7db272d0e5e71b300be1517b0a1dc7cf035944675eaed7066d41e883/pyobjc_framework_ServiceManagement-11.0-py2.py3-none-any.whl", hash = "sha256:35cfd7a369a120fa55e64b719a2dda00295b2cc6ddab16ffa8939f4326d1b37d", size = 5254, upload-time = "2025-01-14T18:59:41.438Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/26c5d63d131e3e415815bfbb4bd035ba10d45f0d87733646221966871b6b/pyobjc_framework_ServiceManagement-11.0-py3-none-any.whl", hash = "sha256:7ec19c9632f67d589ad37815d001e8e443d92e75001c370486a1070a4359e166", size = 5322, upload-time = "2025-01-14T18:59:42.585Z" }, ] [[package]] @@ -3958,10 +3958,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-sharedwithyoucore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696, upload_time = "2025-01-14T19:05:31.396Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696, upload-time = "2025-01-14T19:05:31.396Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/ab/391ef0de3021997ec9a12d8044c0b7e884780a9bead7f847254e06d0f075/pyobjc_framework_SharedWithYou-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dac74375d3dc18d67cae46f3f16a45cef699b1976a4012827c0f15256da55df", size = 8606, upload_time = "2025-01-14T18:59:44.537Z" }, - { url = "https://files.pythonhosted.org/packages/cf/04/6a3eb12bf9c35f3063be678f36430beb92b7e2683f4b952596396473a74d/pyobjc_framework_SharedWithYou-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6076a0893a3597e054918c136f3391671a225a37fe1b1a070046817e3a232954", size = 8629, upload_time = "2025-01-14T18:59:45.579Z" }, + { url = "https://files.pythonhosted.org/packages/3c/ab/391ef0de3021997ec9a12d8044c0b7e884780a9bead7f847254e06d0f075/pyobjc_framework_SharedWithYou-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6dac74375d3dc18d67cae46f3f16a45cef699b1976a4012827c0f15256da55df", size = 8606, upload-time = "2025-01-14T18:59:44.537Z" }, + { url = "https://files.pythonhosted.org/packages/cf/04/6a3eb12bf9c35f3063be678f36430beb92b7e2683f4b952596396473a74d/pyobjc_framework_SharedWithYou-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6076a0893a3597e054918c136f3391671a225a37fe1b1a070046817e3a232954", size = 8629, upload-time = "2025-01-14T18:59:45.579Z" }, ] [[package]] @@ -3972,10 +3972,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877, upload_time = "2025-01-14T19:05:32.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877, upload-time = "2025-01-14T19:05:32.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/40/69ae712e223991cd975c1f8ba2b00a5aa4c129ac0e76838b4d936740e4c7/pyobjc_framework_SharedWithYouCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:46cd00a97c5fec747ef057000daa88495699ea5d5d6fe1f302bfb89b2d431645", size = 8366, upload_time = "2025-01-14T18:59:55.641Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ce/500ad643f2d07e8ef065e8ddc5a08954f5d59cc199c89b700581eaf821ee/pyobjc_framework_SharedWithYouCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8b5f180371a63da718fe6c3b58e7613c6b2adf9b483cefbf6d9467eb8ac2f0ca", size = 8380, upload_time = "2025-01-14T18:59:56.546Z" }, + { url = "https://files.pythonhosted.org/packages/21/40/69ae712e223991cd975c1f8ba2b00a5aa4c129ac0e76838b4d936740e4c7/pyobjc_framework_SharedWithYouCore-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:46cd00a97c5fec747ef057000daa88495699ea5d5d6fe1f302bfb89b2d431645", size = 8366, upload-time = "2025-01-14T18:59:55.641Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ce/500ad643f2d07e8ef065e8ddc5a08954f5d59cc199c89b700581eaf821ee/pyobjc_framework_SharedWithYouCore-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8b5f180371a63da718fe6c3b58e7613c6b2adf9b483cefbf6d9467eb8ac2f0ca", size = 8380, upload-time = "2025-01-14T18:59:56.546Z" }, ] [[package]] @@ -3986,10 +3986,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172, upload_time = "2025-01-14T19:05:34.497Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172, upload-time = "2025-01-14T19:05:34.497Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/81/edfcd4be626aae356dd1b991f521eaeffa1798e91ddae9e7d9ae8ed371d1/pyobjc_framework_ShazamKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ecdc2392d7e8d6e2540c7ad3073a229d08b0818c5dd044a26c93b765ce9868aa", size = 8411, upload_time = "2025-01-14T19:00:02.908Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/f3d2ae7a604e3e3c0de93ed229895be6757edfa0cc76f2a44670f28a81c8/pyobjc_framework_ShazamKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ef79d863cc7d4023aa552f55d4120653eceed862baf1edba8e08b1af10fab036", size = 8419, upload_time = "2025-01-14T19:00:05.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/81/edfcd4be626aae356dd1b991f521eaeffa1798e91ddae9e7d9ae8ed371d1/pyobjc_framework_ShazamKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ecdc2392d7e8d6e2540c7ad3073a229d08b0818c5dd044a26c93b765ce9868aa", size = 8411, upload-time = "2025-01-14T19:00:02.908Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/f3d2ae7a604e3e3c0de93ed229895be6757edfa0cc76f2a44670f28a81c8/pyobjc_framework_ShazamKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ef79d863cc7d4023aa552f55d4120653eceed862baf1edba8e08b1af10fab036", size = 8419, upload-time = "2025-01-14T19:00:05.081Z" }, ] [[package]] @@ -4000,10 +4000,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806, upload_time = "2025-01-14T19:05:35.394Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806, upload-time = "2025-01-14T19:05:35.394Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/1d/2cc0f753ac8b1f5c15cfa9201d8584ff4de6dc940fc954cd9c52d1a615f9/pyobjc_framework_Social-11.0-py2.py3-none-any.whl", hash = "sha256:aa379009738afb0d6abc0347e8189f7f316109e9dfcb904f7f14e6b7c3d5bad8", size = 4362, upload_time = "2025-01-14T19:00:10.058Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/b762b1f9429f8ea0df754e7d58bafd48d73e5527b0423e67570661a7907e/pyobjc_framework_Social-11.0-py3-none-any.whl", hash = "sha256:94db183e8b3ad21272a1ba24e9cda763d603c6021fd80a96d00ce78b6b94e1c2", size = 4428, upload_time = "2025-01-14T19:00:11.242Z" }, + { url = "https://files.pythonhosted.org/packages/46/1d/2cc0f753ac8b1f5c15cfa9201d8584ff4de6dc940fc954cd9c52d1a615f9/pyobjc_framework_Social-11.0-py2.py3-none-any.whl", hash = "sha256:aa379009738afb0d6abc0347e8189f7f316109e9dfcb904f7f14e6b7c3d5bad8", size = 4362, upload-time = "2025-01-14T19:00:10.058Z" }, + { url = "https://files.pythonhosted.org/packages/a8/25/b762b1f9429f8ea0df754e7d58bafd48d73e5527b0423e67570661a7907e/pyobjc_framework_Social-11.0-py3-none-any.whl", hash = "sha256:94db183e8b3ad21272a1ba24e9cda763d603c6021fd80a96d00ce78b6b94e1c2", size = 4428, upload-time = "2025-01-14T19:00:11.242Z" }, ] [[package]] @@ -4014,10 +4014,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789, upload_time = "2025-01-14T19:05:36.576Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789, upload-time = "2025-01-14T19:05:36.576Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/d4/91afb41c514d1e236567b971a981f96c1d20f16eb0658256369c53a4bf45/pyobjc_framework_SoundAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:5969096cadb07f9ba9855cedf6f53674ddb030a324b4981091834d1b31c8c27e", size = 4111, upload_time = "2025-01-14T19:00:13.327Z" }, - { url = "https://files.pythonhosted.org/packages/af/7a/f960ad1e727f6d917e6c84b7383f3eacbb2948bc60396be3bce40cbd8128/pyobjc_framework_SoundAnalysis-11.0-py3-none-any.whl", hash = "sha256:70f70923756e118203cde4ac25083a34ead69a6034baed9c694a36f5fe2325f3", size = 4182, upload_time = "2025-01-14T19:00:15.68Z" }, + { url = "https://files.pythonhosted.org/packages/ab/d4/91afb41c514d1e236567b971a981f96c1d20f16eb0658256369c53a4bf45/pyobjc_framework_SoundAnalysis-11.0-py2.py3-none-any.whl", hash = "sha256:5969096cadb07f9ba9855cedf6f53674ddb030a324b4981091834d1b31c8c27e", size = 4111, upload-time = "2025-01-14T19:00:13.327Z" }, + { url = "https://files.pythonhosted.org/packages/af/7a/f960ad1e727f6d917e6c84b7383f3eacbb2948bc60396be3bce40cbd8128/pyobjc_framework_SoundAnalysis-11.0-py3-none-any.whl", hash = "sha256:70f70923756e118203cde4ac25083a34ead69a6034baed9c694a36f5fe2325f3", size = 4182, upload-time = "2025-01-14T19:00:15.68Z" }, ] [[package]] @@ -4028,10 +4028,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620, upload_time = "2025-01-14T19:05:38.391Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620, upload-time = "2025-01-14T19:05:38.391Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/85/e989076ff0cd40c7cfb3ed7d621703de11bfd8286f1729aca759db1f42a3/pyobjc_framework_Speech-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:353179210683e38bfbd675df6a35eec46b30ce30b7291bcb07a5cadaf11a3bd7", size = 9016, upload_time = "2025-01-14T19:00:17.661Z" }, - { url = "https://files.pythonhosted.org/packages/00/03/827acde068787c2318981e2bfef2c3cadbe8552434ccc0634b30084ef914/pyobjc_framework_Speech-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:134e08025f4638e428602f7e16bbec94b00477eec090316138d758a86e10fd5f", size = 9037, upload_time = "2025-01-14T19:00:21.186Z" }, + { url = "https://files.pythonhosted.org/packages/b0/85/e989076ff0cd40c7cfb3ed7d621703de11bfd8286f1729aca759db1f42a3/pyobjc_framework_Speech-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:353179210683e38bfbd675df6a35eec46b30ce30b7291bcb07a5cadaf11a3bd7", size = 9016, upload-time = "2025-01-14T19:00:17.661Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/827acde068787c2318981e2bfef2c3cadbe8552434ccc0634b30084ef914/pyobjc_framework_Speech-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:134e08025f4638e428602f7e16bbec94b00477eec090316138d758a86e10fd5f", size = 9037, upload-time = "2025-01-14T19:00:21.186Z" }, ] [[package]] @@ -4043,10 +4043,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851, upload_time = "2025-01-14T19:05:39.709Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851, upload-time = "2025-01-14T19:05:39.709Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/80/319f156ac6f6cab0dbc85881d81a74d4a7f17913256338683ae8d9ed56c4/pyobjc_framework_SpriteKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d0971a7a85786edc521ab897bdb0c78696278e6417bf389abdfe2151358e854", size = 18077, upload_time = "2025-01-14T19:00:26.815Z" }, - { url = "https://files.pythonhosted.org/packages/bb/09/303d76844a10745cdbac1ff76c2c8630c1ef46455014562dc79aaa72a6e3/pyobjc_framework_SpriteKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0da5f2b52636a2f04fc38a123fed9d7f8d6fd353df027c51c0bfc91e244a9d2b", size = 18145, upload_time = "2025-01-14T19:00:27.956Z" }, + { url = "https://files.pythonhosted.org/packages/e1/80/319f156ac6f6cab0dbc85881d81a74d4a7f17913256338683ae8d9ed56c4/pyobjc_framework_SpriteKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d0971a7a85786edc521ab897bdb0c78696278e6417bf389abdfe2151358e854", size = 18077, upload-time = "2025-01-14T19:00:26.815Z" }, + { url = "https://files.pythonhosted.org/packages/bb/09/303d76844a10745cdbac1ff76c2c8630c1ef46455014562dc79aaa72a6e3/pyobjc_framework_SpriteKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0da5f2b52636a2f04fc38a123fed9d7f8d6fd353df027c51c0bfc91e244a9d2b", size = 18145, upload-time = "2025-01-14T19:00:27.956Z" }, ] [[package]] @@ -4057,10 +4057,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855, upload_time = "2025-01-14T19:05:41.605Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855, upload-time = "2025-01-14T19:05:41.605Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/40/af53ad7781515866003c2c71056a053d2f033cf2aa31920a8a1fdb829d7a/pyobjc_framework_StoreKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1d51a05a5e0277c542978b1f5a6aa33331359de7c0a2cf0ad922760b36e5066a", size = 11655, upload_time = "2025-01-14T19:00:32.894Z" }, - { url = "https://files.pythonhosted.org/packages/f3/11/ba3259d3b22980e08c5e8255a48cc97180bec47d72ffbbd41ab699df39b1/pyobjc_framework_StoreKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:29269183e91043bbfee79851ae712073feba1e10845b8deeb7e6aaa20cfb3cf4", size = 11680, upload_time = "2025-01-14T19:00:35.36Z" }, + { url = "https://files.pythonhosted.org/packages/ab/40/af53ad7781515866003c2c71056a053d2f033cf2aa31920a8a1fdb829d7a/pyobjc_framework_StoreKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1d51a05a5e0277c542978b1f5a6aa33331359de7c0a2cf0ad922760b36e5066a", size = 11655, upload-time = "2025-01-14T19:00:32.894Z" }, + { url = "https://files.pythonhosted.org/packages/f3/11/ba3259d3b22980e08c5e8255a48cc97180bec47d72ffbbd41ab699df39b1/pyobjc_framework_StoreKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:29269183e91043bbfee79851ae712073feba1e10845b8deeb7e6aaa20cfb3cf4", size = 11680, upload-time = "2025-01-14T19:00:35.36Z" }, ] [[package]] @@ -4071,10 +4071,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682, upload_time = "2025-01-14T19:05:45.727Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682, upload-time = "2025-01-14T19:05:45.727Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/ff/341d44f5347d48491682bece366444f3e230e33109266dcc6a17e6a7fc3d/pyobjc_framework_Symbols-11.0-py2.py3-none-any.whl", hash = "sha256:f1490823f40a8a540ac10628190695f27a717343914fe5db5fafa500f7c7bf44", size = 3263, upload_time = "2025-01-14T19:00:41.055Z" }, - { url = "https://files.pythonhosted.org/packages/94/a4/c21353872a2fc643206a44ac55b92b5b7533cdb2cb26c44a9048debc295a/pyobjc_framework_Symbols-11.0-py3-none-any.whl", hash = "sha256:0919e85fcf6f420f61d8d9a67cafa2ab4678666441ef4f001b31f5457900b314", size = 3335, upload_time = "2025-01-14T19:00:43.294Z" }, + { url = "https://files.pythonhosted.org/packages/66/ff/341d44f5347d48491682bece366444f3e230e33109266dcc6a17e6a7fc3d/pyobjc_framework_Symbols-11.0-py2.py3-none-any.whl", hash = "sha256:f1490823f40a8a540ac10628190695f27a717343914fe5db5fafa500f7c7bf44", size = 3263, upload-time = "2025-01-14T19:00:41.055Z" }, + { url = "https://files.pythonhosted.org/packages/94/a4/c21353872a2fc643206a44ac55b92b5b7533cdb2cb26c44a9048debc295a/pyobjc_framework_Symbols-11.0-py3-none-any.whl", hash = "sha256:0919e85fcf6f420f61d8d9a67cafa2ab4678666441ef4f001b31f5457900b314", size = 3335, upload-time = "2025-01-14T19:00:43.294Z" }, ] [[package]] @@ -4086,10 +4086,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-coredata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453, upload_time = "2025-01-14T19:05:46.559Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453, upload-time = "2025-01-14T19:05:46.559Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/9b/484db4eed6b1e29e0d69275bd459ab21a6b3f98e8b2ce61beeb9971303ca/pyobjc_framework_SyncServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89a398df6518cff1c63b7cccf3025e388f3ef299645734112c5aa1ac5f7ca30a", size = 13989, upload_time = "2025-01-14T19:00:45.216Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d8/dc86d708434b7cb59825c56549e64b118ba4b8584d2eb5a1514d1cd5d1bd/pyobjc_framework_SyncServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e870e82ed34c43607cc50dbae57a81dd419b75abc06670630cbbf41ae6e1402c", size = 14008, upload_time = "2025-01-14T19:00:46.188Z" }, + { url = "https://files.pythonhosted.org/packages/15/9b/484db4eed6b1e29e0d69275bd459ab21a6b3f98e8b2ce61beeb9971303ca/pyobjc_framework_SyncServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89a398df6518cff1c63b7cccf3025e388f3ef299645734112c5aa1ac5f7ca30a", size = 13989, upload-time = "2025-01-14T19:00:45.216Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d8/dc86d708434b7cb59825c56549e64b118ba4b8584d2eb5a1514d1cd5d1bd/pyobjc_framework_SyncServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e870e82ed34c43607cc50dbae57a81dd419b75abc06670630cbbf41ae6e1402c", size = 14008, upload-time = "2025-01-14T19:00:46.188Z" }, ] [[package]] @@ -4100,10 +4100,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949, upload_time = "2025-01-14T19:05:47.466Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949, upload-time = "2025-01-14T19:05:47.466Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/8f/1b5f7e8e848d2c84204da08d5c63e42feff86b26cd508da7a4f95960b842/pyobjc_framework_SystemConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89d3c54abedcedbc2ce52c31ff4878251ca54a8535407ed6bd6584ce099c148b", size = 21836, upload_time = "2025-01-14T19:00:51.698Z" }, - { url = "https://files.pythonhosted.org/packages/6d/49/8660b3d0a46ac2f88e73cec3d10e21885b107f54635680ef0c677ac5cf3e/pyobjc_framework_SystemConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8cbcb9662dbb5a034cfc5a44adaf2a0226a2985ae299a4ef4fd75bb49f30f5a0", size = 21727, upload_time = "2025-01-14T19:00:52.685Z" }, + { url = "https://files.pythonhosted.org/packages/28/8f/1b5f7e8e848d2c84204da08d5c63e42feff86b26cd508da7a4f95960b842/pyobjc_framework_SystemConfiguration-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:89d3c54abedcedbc2ce52c31ff4878251ca54a8535407ed6bd6584ce099c148b", size = 21836, upload-time = "2025-01-14T19:00:51.698Z" }, + { url = "https://files.pythonhosted.org/packages/6d/49/8660b3d0a46ac2f88e73cec3d10e21885b107f54635680ef0c677ac5cf3e/pyobjc_framework_SystemConfiguration-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8cbcb9662dbb5a034cfc5a44adaf2a0226a2985ae299a4ef4fd75bb49f30f5a0", size = 21727, upload-time = "2025-01-14T19:00:52.685Z" }, ] [[package]] @@ -4114,10 +4114,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531, upload_time = "2025-01-14T19:05:48.463Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531, upload-time = "2025-01-14T19:05:48.463Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/3c/8f91b89554ef3127e037d90b3ef83c77a994bb889b7884a995756cd06b63/pyobjc_framework_SystemExtensions-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7a2ec417fa0d383cc066bc292541aa78fd2aec9cca83a98d41b7982f185d1f7", size = 8975, upload_time = "2025-01-14T19:00:57.822Z" }, - { url = "https://files.pythonhosted.org/packages/21/8c/cf2a018b5f1ecd216f8cb26a3b6fbe590d08de81a6c6b4658e001a203886/pyobjc_framework_SystemExtensions-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:62b99c6bd88bce642960fc2b9d5903fbfca680d16be9a4565a883eb4ba17ca5e", size = 8999, upload_time = "2025-01-14T19:00:58.865Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/8f91b89554ef3127e037d90b3ef83c77a994bb889b7884a995756cd06b63/pyobjc_framework_SystemExtensions-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7a2ec417fa0d383cc066bc292541aa78fd2aec9cca83a98d41b7982f185d1f7", size = 8975, upload-time = "2025-01-14T19:00:57.822Z" }, + { url = "https://files.pythonhosted.org/packages/21/8c/cf2a018b5f1ecd216f8cb26a3b6fbe590d08de81a6c6b4658e001a203886/pyobjc_framework_SystemExtensions-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:62b99c6bd88bce642960fc2b9d5903fbfca680d16be9a4565a883eb4ba17ca5e", size = 8999, upload-time = "2025-01-14T19:00:58.865Z" }, ] [[package]] @@ -4128,10 +4128,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820, upload_time = "2025-01-14T19:05:49.307Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820, upload-time = "2025-01-14T19:05:49.307Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/a9/908184da457e33a110de7d2d262efa69beaba6db243342df5654da03566b/pyobjc_framework_ThreadNetwork-11.0-py2.py3-none-any.whl", hash = "sha256:950d46a009cb992b12dbd8169a0450d8cc101fc982e03e6543078c6d7790e353", size = 3700, upload_time = "2025-01-14T19:01:03.254Z" }, - { url = "https://files.pythonhosted.org/packages/59/d4/4694fc7a627d2b6b37c51433ba7f02a39a283a445dc77349b82fe24534f1/pyobjc_framework_ThreadNetwork-11.0-py3-none-any.whl", hash = "sha256:1218649e4f488ca411af13b74f1dee1e7a178169e0f5963342ba8a7c46037ea7", size = 3770, upload_time = "2025-01-14T19:01:05.456Z" }, + { url = "https://files.pythonhosted.org/packages/55/a9/908184da457e33a110de7d2d262efa69beaba6db243342df5654da03566b/pyobjc_framework_ThreadNetwork-11.0-py2.py3-none-any.whl", hash = "sha256:950d46a009cb992b12dbd8169a0450d8cc101fc982e03e6543078c6d7790e353", size = 3700, upload-time = "2025-01-14T19:01:03.254Z" }, + { url = "https://files.pythonhosted.org/packages/59/d4/4694fc7a627d2b6b37c51433ba7f02a39a283a445dc77349b82fe24534f1/pyobjc_framework_ThreadNetwork-11.0-py3-none-any.whl", hash = "sha256:1218649e4f488ca411af13b74f1dee1e7a178169e0f5963342ba8a7c46037ea7", size = 3770, upload-time = "2025-01-14T19:01:05.456Z" }, ] [[package]] @@ -4142,10 +4142,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860, upload_time = "2025-01-14T19:05:50.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860, upload-time = "2025-01-14T19:05:50.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/f2/094888af07fb7f0443996e5d91915e74b87e8705b599b68b516a0e94a63d/pyobjc_framework_UniformTypeIdentifiers-11.0-py2.py3-none-any.whl", hash = "sha256:acffb86e8b03b66c49274236b3df3a254cacd32b9f25bd7a5bd59baaaf738624", size = 4841, upload_time = "2025-01-14T19:01:06.404Z" }, - { url = "https://files.pythonhosted.org/packages/88/9c/4cc0522cc546e6a3bf8a921e3a9f0ed078e3cf907d616760d9f3d7754919/pyobjc_framework_UniformTypeIdentifiers-11.0-py3-none-any.whl", hash = "sha256:a3097f186c7e231b19218a3ceecb3b70a8f2b2e9e642ef409dc7a195a30c869e", size = 4910, upload_time = "2025-01-14T19:01:07.393Z" }, + { url = "https://files.pythonhosted.org/packages/82/f2/094888af07fb7f0443996e5d91915e74b87e8705b599b68b516a0e94a63d/pyobjc_framework_UniformTypeIdentifiers-11.0-py2.py3-none-any.whl", hash = "sha256:acffb86e8b03b66c49274236b3df3a254cacd32b9f25bd7a5bd59baaaf738624", size = 4841, upload-time = "2025-01-14T19:01:06.404Z" }, + { url = "https://files.pythonhosted.org/packages/88/9c/4cc0522cc546e6a3bf8a921e3a9f0ed078e3cf907d616760d9f3d7754919/pyobjc_framework_UniformTypeIdentifiers-11.0-py3-none-any.whl", hash = "sha256:a3097f186c7e231b19218a3ceecb3b70a8f2b2e9e642ef409dc7a195a30c869e", size = 4910, upload-time = "2025-01-14T19:01:07.393Z" }, ] [[package]] @@ -4156,10 +4156,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857, upload_time = "2025-01-14T19:05:52.42Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857, upload-time = "2025-01-14T19:05:52.42Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/bf/5545d5c9d0d10a603ad406a5ce727de6a47daace9c38d4484818611599f3/pyobjc_framework_UserNotifications-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bf78fa37f574f5b43db9b83ca02e82ab45803589f970042afdcd1cb8c01396d", size = 9483, upload_time = "2025-01-14T19:01:09.256Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1e/41f4d18120b2c006f756edde1845a2df45fdbd6957e540f8ebcfae25747f/pyobjc_framework_UserNotifications-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b4c06c3862405e103e964327581c28e5390a2d4cd0cef3d8e64afda03c9f431", size = 9506, upload_time = "2025-01-14T19:01:10.218Z" }, + { url = "https://files.pythonhosted.org/packages/1f/bf/5545d5c9d0d10a603ad406a5ce727de6a47daace9c38d4484818611599f3/pyobjc_framework_UserNotifications-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4bf78fa37f574f5b43db9b83ca02e82ab45803589f970042afdcd1cb8c01396d", size = 9483, upload-time = "2025-01-14T19:01:09.256Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/41f4d18120b2c006f756edde1845a2df45fdbd6957e540f8ebcfae25747f/pyobjc_framework_UserNotifications-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0b4c06c3862405e103e964327581c28e5390a2d4cd0cef3d8e64afda03c9f431", size = 9506, upload-time = "2025-01-14T19:01:10.218Z" }, ] [[package]] @@ -4171,10 +4171,10 @@ dependencies = [ { name = "pyobjc-framework-cocoa" }, { name = "pyobjc-framework-usernotifications" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369, upload_time = "2025-01-14T19:05:54.498Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369, upload-time = "2025-01-14T19:05:54.498Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f7/64c95c6f82e92bb1cbcb8d5c3658c79c954668627eef28f11e76025a3ed1/pyobjc_framework_UserNotificationsUI-11.0-py2.py3-none-any.whl", hash = "sha256:6185d9c9513b6a823cd72dcf40d2fb33bbf0f2c9a98528e0e112580b47ac3632", size = 3856, upload_time = "2025-01-14T19:01:15.43Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c3/e1d64c9e523b5192e0179b6723ee465e74d6c282104a49a67347d527a65d/pyobjc_framework_UserNotificationsUI-11.0-py3-none-any.whl", hash = "sha256:e4439e549265929ddad1feca7b062d00c2d3732470f349cb0d594705e0257919", size = 3932, upload_time = "2025-01-14T19:01:16.486Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f7/64c95c6f82e92bb1cbcb8d5c3658c79c954668627eef28f11e76025a3ed1/pyobjc_framework_UserNotificationsUI-11.0-py2.py3-none-any.whl", hash = "sha256:6185d9c9513b6a823cd72dcf40d2fb33bbf0f2c9a98528e0e112580b47ac3632", size = 3856, upload-time = "2025-01-14T19:01:15.43Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c3/e1d64c9e523b5192e0179b6723ee465e74d6c282104a49a67347d527a65d/pyobjc_framework_UserNotificationsUI-11.0-py3-none-any.whl", hash = "sha256:e4439e549265929ddad1feca7b062d00c2d3732470f349cb0d594705e0257919", size = 3932, upload-time = "2025-01-14T19:01:16.486Z" }, ] [[package]] @@ -4185,10 +4185,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100, upload_time = "2025-01-14T19:05:55.319Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100, upload-time = "2025-01-14T19:05:55.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/82/94650fe5cc68c0c32fe56fe22cd7eb2874b28f987a9e259fac12cbea7705/pyobjc_framework_VideoSubscriberAccount-11.0-py2.py3-none-any.whl", hash = "sha256:1deec8d5a0138ae51b5ca7bfb7f6fe1b0dc3cbb52db3111059708efa5f8a8d04", size = 4637, upload_time = "2025-01-14T19:01:17.365Z" }, - { url = "https://files.pythonhosted.org/packages/61/54/1765507adad1b0c9bc6be10f09b249d425212bc0d9fef1efdfd872ee9807/pyobjc_framework_VideoSubscriberAccount-11.0-py3-none-any.whl", hash = "sha256:0095eddb5fc942f9e049bc4c683cf28c77ea60c60942552c3c48bf74c8fdca9b", size = 4709, upload_time = "2025-01-14T19:01:18.349Z" }, + { url = "https://files.pythonhosted.org/packages/51/82/94650fe5cc68c0c32fe56fe22cd7eb2874b28f987a9e259fac12cbea7705/pyobjc_framework_VideoSubscriberAccount-11.0-py2.py3-none-any.whl", hash = "sha256:1deec8d5a0138ae51b5ca7bfb7f6fe1b0dc3cbb52db3111059708efa5f8a8d04", size = 4637, upload-time = "2025-01-14T19:01:17.365Z" }, + { url = "https://files.pythonhosted.org/packages/61/54/1765507adad1b0c9bc6be10f09b249d425212bc0d9fef1efdfd872ee9807/pyobjc_framework_VideoSubscriberAccount-11.0-py3-none-any.whl", hash = "sha256:0095eddb5fc942f9e049bc4c683cf28c77ea60c60942552c3c48bf74c8fdca9b", size = 4709, upload-time = "2025-01-14T19:01:18.349Z" }, ] [[package]] @@ -4201,10 +4201,10 @@ dependencies = [ { name = "pyobjc-framework-coremedia" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157, upload_time = "2025-01-14T19:05:56.135Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157, upload-time = "2025-01-14T19:05:56.135Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/ae/ff697840bdcf3530e8fba84e2a606813eda1ee90be074f12e2857460cebf/pyobjc_framework_VideoToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:12af56190e65c3b60c6ca14fe69045e5ffb5908ea1363580506eb32603b80855", size = 13446, upload_time = "2025-01-14T19:01:21.066Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ef/9e7230435da47016983a3c9ea7b1d5237b43fce2d8b2b923eb638b7694f5/pyobjc_framework_VideoToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ed7f073bd8dfecca0da6359d5cd871b2f39144883930bddd41ca818447de608", size = 13451, upload_time = "2025-01-14T19:01:22.009Z" }, + { url = "https://files.pythonhosted.org/packages/44/ae/ff697840bdcf3530e8fba84e2a606813eda1ee90be074f12e2857460cebf/pyobjc_framework_VideoToolbox-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:12af56190e65c3b60c6ca14fe69045e5ffb5908ea1363580506eb32603b80855", size = 13446, upload-time = "2025-01-14T19:01:21.066Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ef/9e7230435da47016983a3c9ea7b1d5237b43fce2d8b2b923eb638b7694f5/pyobjc_framework_VideoToolbox-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ed7f073bd8dfecca0da6359d5cd871b2f39144883930bddd41ca818447de608", size = 13451, upload-time = "2025-01-14T19:01:22.009Z" }, ] [[package]] @@ -4215,10 +4215,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144, upload_time = "2025-01-14T19:05:57.086Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144, upload-time = "2025-01-14T19:05:57.086Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/c9/b2f8322d7ced14822270481be5b44f1846aa7c09b4b3cb52517dc1054f4b/pyobjc_framework_Virtualization-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334712792136ffcf3c63a63cea01ce33d60309a82721c95e25f0cc26b95f72cc", size = 13417, upload_time = "2025-01-14T19:01:28.821Z" }, - { url = "https://files.pythonhosted.org/packages/1e/96/d64425811a4ef2c8b38914ea1a91bbd2aa6136bb79989e4821acd6d28e67/pyobjc_framework_Virtualization-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b848b1ab365906b11a507c8146e477c27d2bf56159d49d21fda15b93c2811ec", size = 13430, upload_time = "2025-01-14T19:01:29.733Z" }, + { url = "https://files.pythonhosted.org/packages/6b/c9/b2f8322d7ced14822270481be5b44f1846aa7c09b4b3cb52517dc1054f4b/pyobjc_framework_Virtualization-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334712792136ffcf3c63a63cea01ce33d60309a82721c95e25f0cc26b95f72cc", size = 13417, upload-time = "2025-01-14T19:01:28.821Z" }, + { url = "https://files.pythonhosted.org/packages/1e/96/d64425811a4ef2c8b38914ea1a91bbd2aa6136bb79989e4821acd6d28e67/pyobjc_framework_Virtualization-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b848b1ab365906b11a507c8146e477c27d2bf56159d49d21fda15b93c2811ec", size = 13430, upload-time = "2025-01-14T19:01:29.733Z" }, ] [[package]] @@ -4231,10 +4231,10 @@ dependencies = [ { name = "pyobjc-framework-coreml" }, { name = "pyobjc-framework-quartz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175, upload_time = "2025-01-14T19:05:58.013Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175, upload-time = "2025-01-14T19:05:58.013Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/84/d23a745d46858409a1dca3e7f5cb3089c148ebb8d42e7a6289e1972ad650/pyobjc_framework_Vision-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ca7cc48332d804a02b5b17f31bed52dd4b7c323f9e4ff4b4e7ecd35d39cc0759", size = 21754, upload_time = "2025-01-14T19:01:35.504Z" }, - { url = "https://files.pythonhosted.org/packages/3a/80/6db9fc2a3f8b991860156f4700f979ad8aa1e9617b0efa720ee3b52e3602/pyobjc_framework_Vision-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1b07aa867dda47d2a4883cd969e248039988b49190ba097cbe9747156b5d1f30", size = 17099, upload_time = "2025-01-14T19:01:37.457Z" }, + { url = "https://files.pythonhosted.org/packages/7f/84/d23a745d46858409a1dca3e7f5cb3089c148ebb8d42e7a6289e1972ad650/pyobjc_framework_Vision-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ca7cc48332d804a02b5b17f31bed52dd4b7c323f9e4ff4b4e7ecd35d39cc0759", size = 21754, upload-time = "2025-01-14T19:01:35.504Z" }, + { url = "https://files.pythonhosted.org/packages/3a/80/6db9fc2a3f8b991860156f4700f979ad8aa1e9617b0efa720ee3b52e3602/pyobjc_framework_Vision-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1b07aa867dda47d2a4883cd969e248039988b49190ba097cbe9747156b5d1f30", size = 17099, upload-time = "2025-01-14T19:01:37.457Z" }, ] [[package]] @@ -4245,10 +4245,10 @@ dependencies = [ { name = "pyobjc-core" }, { name = "pyobjc-framework-cocoa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210, upload_time = "2025-01-14T19:05:59.3Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210, upload-time = "2025-01-14T19:05:59.3Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/63/6f04faa75c4c39c54007b256a8e13838c1de213d487f561937d342ec2eac/pyobjc_framework_WebKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:163abaa5a665b59626ef20cdc3dcc5e2e3fcd9830d5fc328507e13f663acd0ed", size = 44940, upload_time = "2025-01-14T19:01:44.396Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/934f03510e7f49454fbf6eeff8ad2eca5d8bfbe71aa4b8a034f8132af2fa/pyobjc_framework_WebKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2e4911519e94822011d99fdb9addf4a176f45a79808dab18dc303293f4590f7c", size = 44901, upload_time = "2025-01-14T19:01:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/47/63/6f04faa75c4c39c54007b256a8e13838c1de213d487f561937d342ec2eac/pyobjc_framework_WebKit-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:163abaa5a665b59626ef20cdc3dcc5e2e3fcd9830d5fc328507e13f663acd0ed", size = 44940, upload-time = "2025-01-14T19:01:44.396Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/934f03510e7f49454fbf6eeff8ad2eca5d8bfbe71aa4b8a034f8132af2fa/pyobjc_framework_WebKit-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2e4911519e94822011d99fdb9addf4a176f45a79808dab18dc303293f4590f7c", size = 44901, upload-time = "2025-01-14T19:01:45.476Z" }, ] [[package]] @@ -4260,18 +4260,18 @@ dependencies = [ { name = "platformdirs" }, { name = "pytools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload_time = "2025-01-22T00:16:58.421Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/ce/c40c6248b29195397a6e176615c24a8047cdd3afe847932a1f27603c1b14/pyopencl-2025.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:a302e4ee1bb19ff244f5ae2b5a83a98977daa13f31929a85f23723020a4bec01", size = 424117, upload_time = "2025-01-22T00:16:08.957Z" }, - { url = "https://files.pythonhosted.org/packages/71/dd/8dd4e18396c705567be7eda65234932f8eb7e975cc15ae167265ed9c7d20/pyopencl-2025.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:48527fc5a250b9e89f2eaaa1c9423e1bc15ff181bd41ffa1e29e31890dc1d3b7", size = 408327, upload_time = "2025-01-22T00:16:10.42Z" }, - { url = "https://files.pythonhosted.org/packages/47/7b/270c4e6765b675eaa97af8ff0c964e21dc74ac2a2f04e2c24431c91ce382/pyopencl-2025.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95490199c490e47b245b88e64e06f95272502d13810da172ac3b0f0340cf8715", size = 724636, upload_time = "2025-01-22T00:16:12.881Z" }, - { url = "https://files.pythonhosted.org/packages/0d/55/996d7877793acfc340678a71dc98151a8c39dbb289cf24ecae08c0af68eb/pyopencl-2025.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd2cb3c031beeec93cf660c8809d6ad58a2cde7dcd95ae12b4b2da6ac8e2da41", size = 1173429, upload_time = "2025-01-22T00:16:14.466Z" }, - { url = "https://files.pythonhosted.org/packages/3d/7c/d2a89b1c24c318375856e8b7611bc03ddf687134f68ddbb387496453eda8/pyopencl-2025.1-cp311-cp311-win_amd64.whl", hash = "sha256:d8d3cdb02dc8750a9cc11c5b37f219da1f61b4216af4a830eb52b451a0e9f9a7", size = 457877, upload_time = "2025-01-22T00:16:17.21Z" }, - { url = "https://files.pythonhosted.org/packages/02/c0/d9536211ecfddd3bdf7eaa1658d085fcd92120061ac6f4e662a5062660ff/pyopencl-2025.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:88c564d94a5067ab6b9429a7d92b655254da8b2b5a33c7e30e10c5a0cf3154e1", size = 425706, upload_time = "2025-01-22T00:16:18.771Z" }, - { url = "https://files.pythonhosted.org/packages/63/b9/3e6dd574cc9ffb2271be135ecdb36cd6aea70a1f74e80539b048072a256a/pyopencl-2025.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f204cd6974ca19e7ffe14f21afac9967b06a6718f3aecbbc4a4df313e8e7ebc2", size = 408163, upload_time = "2025-01-22T00:16:20.282Z" }, - { url = "https://files.pythonhosted.org/packages/a4/4d/7f6f2e24b12585b81fd49f689d21ba62187656d061e3cb43840f12097dad/pyopencl-2025.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce8d1b3fd2046e89377b754117fdb3505f45edacfda6ad2b3a29670c0526ad1", size = 719348, upload_time = "2025-01-22T00:16:22.15Z" }, - { url = "https://files.pythonhosted.org/packages/f0/45/3c93510819859e047d494dd8f4ed80c26378bb964a8e5e850cc079cc1f6e/pyopencl-2025.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb0b7b775424f0c4c929a00f09eb351075ea9e4d2b1c80afe37d09bec218ee9", size = 1170733, upload_time = "2025-01-22T00:16:24.575Z" }, - { url = "https://files.pythonhosted.org/packages/91/ba/b745715085ef893fa7d1c7e04c95e3e554b6b27b2fb0800d6bbca563b43c/pyopencl-2025.1-cp312-cp312-win_amd64.whl", hash = "sha256:78f2a58d2e177793fb5a7b9c8a574e3a5f1d178c4df713969d1b08341c817d60", size = 457762, upload_time = "2025-01-22T00:16:27.334Z" }, + { url = "https://files.pythonhosted.org/packages/99/ce/c40c6248b29195397a6e176615c24a8047cdd3afe847932a1f27603c1b14/pyopencl-2025.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:a302e4ee1bb19ff244f5ae2b5a83a98977daa13f31929a85f23723020a4bec01", size = 424117, upload-time = "2025-01-22T00:16:08.957Z" }, + { url = "https://files.pythonhosted.org/packages/71/dd/8dd4e18396c705567be7eda65234932f8eb7e975cc15ae167265ed9c7d20/pyopencl-2025.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:48527fc5a250b9e89f2eaaa1c9423e1bc15ff181bd41ffa1e29e31890dc1d3b7", size = 408327, upload-time = "2025-01-22T00:16:10.42Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/270c4e6765b675eaa97af8ff0c964e21dc74ac2a2f04e2c24431c91ce382/pyopencl-2025.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95490199c490e47b245b88e64e06f95272502d13810da172ac3b0f0340cf8715", size = 724636, upload-time = "2025-01-22T00:16:12.881Z" }, + { url = "https://files.pythonhosted.org/packages/0d/55/996d7877793acfc340678a71dc98151a8c39dbb289cf24ecae08c0af68eb/pyopencl-2025.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd2cb3c031beeec93cf660c8809d6ad58a2cde7dcd95ae12b4b2da6ac8e2da41", size = 1173429, upload-time = "2025-01-22T00:16:14.466Z" }, + { url = "https://files.pythonhosted.org/packages/3d/7c/d2a89b1c24c318375856e8b7611bc03ddf687134f68ddbb387496453eda8/pyopencl-2025.1-cp311-cp311-win_amd64.whl", hash = "sha256:d8d3cdb02dc8750a9cc11c5b37f219da1f61b4216af4a830eb52b451a0e9f9a7", size = 457877, upload-time = "2025-01-22T00:16:17.21Z" }, + { url = "https://files.pythonhosted.org/packages/02/c0/d9536211ecfddd3bdf7eaa1658d085fcd92120061ac6f4e662a5062660ff/pyopencl-2025.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:88c564d94a5067ab6b9429a7d92b655254da8b2b5a33c7e30e10c5a0cf3154e1", size = 425706, upload-time = "2025-01-22T00:16:18.771Z" }, + { url = "https://files.pythonhosted.org/packages/63/b9/3e6dd574cc9ffb2271be135ecdb36cd6aea70a1f74e80539b048072a256a/pyopencl-2025.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f204cd6974ca19e7ffe14f21afac9967b06a6718f3aecbbc4a4df313e8e7ebc2", size = 408163, upload-time = "2025-01-22T00:16:20.282Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4d/7f6f2e24b12585b81fd49f689d21ba62187656d061e3cb43840f12097dad/pyopencl-2025.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce8d1b3fd2046e89377b754117fdb3505f45edacfda6ad2b3a29670c0526ad1", size = 719348, upload-time = "2025-01-22T00:16:22.15Z" }, + { url = "https://files.pythonhosted.org/packages/f0/45/3c93510819859e047d494dd8f4ed80c26378bb964a8e5e850cc079cc1f6e/pyopencl-2025.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb0b7b775424f0c4c929a00f09eb351075ea9e4d2b1c80afe37d09bec218ee9", size = 1170733, upload-time = "2025-01-22T00:16:24.575Z" }, + { url = "https://files.pythonhosted.org/packages/91/ba/b745715085ef893fa7d1c7e04c95e3e554b6b27b2fb0800d6bbca563b43c/pyopencl-2025.1-cp312-cp312-win_amd64.whl", hash = "sha256:78f2a58d2e177793fb5a7b9c8a574e3a5f1d178c4df713969d1b08341c817d60", size = 457762, upload-time = "2025-01-22T00:16:27.334Z" }, ] [[package]] @@ -4281,37 +4281,37 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5d/70/ff56a63248562e77c0c8ee4aefc3224258f1856977e0c1472672b62dadb8/pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95", size = 184323, upload_time = "2024-07-20T17:26:31.252Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/70/ff56a63248562e77c0c8ee4aefc3224258f1856977e0c1472672b62dadb8/pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95", size = 184323, upload-time = "2024-07-20T17:26:31.252Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/dd/e0aa7ebef5168c75b772eda64978c597a9129b46be17779054652a7999e4/pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d", size = 58390, upload_time = "2024-07-20T17:26:29.057Z" }, + { url = "https://files.pythonhosted.org/packages/d9/dd/e0aa7ebef5168c75b772eda64978c597a9129b46be17779054652a7999e4/pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d", size = 58390, upload-time = "2024-07-20T17:26:29.057Z" }, ] [[package]] name = "pyparsing" version = "3.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload_time = "2025-03-25T05:01:28.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload_time = "2025-03-25T05:01:24.908Z" }, + { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, ] [[package]] name = "pyperclip" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload_time = "2024-06-18T20:38:48.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961, upload-time = "2024-06-18T20:38:48.401Z" } [[package]] name = "pyprof2calltree" version = "1.4.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/2a/e9a76261183b4b5e059a6625d7aae0bcb0a77622bc767d4497148ce2e218/pyprof2calltree-1.4.5.tar.gz", hash = "sha256:a635672ff31677486350b2be9a823ef92f740e6354a6aeda8fa4a8a3768e8f2f", size = 10080, upload_time = "2020-04-19T10:39:09.819Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/2a/e9a76261183b4b5e059a6625d7aae0bcb0a77622bc767d4497148ce2e218/pyprof2calltree-1.4.5.tar.gz", hash = "sha256:a635672ff31677486350b2be9a823ef92f740e6354a6aeda8fa4a8a3768e8f2f", size = 10080, upload-time = "2020-04-19T10:39:09.819Z" } [[package]] name = "pyrect" version = "0.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219, upload_time = "2022-03-16T04:45:52.36Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/04/2ba023d5f771b645f7be0c281cdacdcd939fe13d1deb331fc5ed1a6b3a98/PyRect-0.2.0.tar.gz", hash = "sha256:f65155f6df9b929b67caffbd57c0947c5ae5449d3b580d178074bffb47a09b78", size = 17219, upload-time = "2022-03-16T04:45:52.36Z" } [[package]] name = "pyscreeze" @@ -4320,15 +4320,15 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pillow", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826, upload_time = "2024-08-20T23:03:07.291Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f0/cb456ac4f1a73723d5b866933b7986f02bacea27516629c00f8e7da94c2d/pyscreeze-1.0.1.tar.gz", hash = "sha256:cf1662710f1b46aa5ff229ee23f367da9e20af4a78e6e365bee973cad0ead4be", size = 27826, upload-time = "2024-08-20T23:03:07.291Z" } [[package]] name = "pyserial" version = "3.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload_time = "2020-11-23T03:59:15.045Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload_time = "2020-11-23T03:59:13.41Z" }, + { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, ] [[package]] @@ -4341,9 +4341,9 @@ dependencies = [ { name = "packaging" }, { name = "pluggy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload_time = "2025-03-02T12:54:54.503Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891, upload-time = "2025-03-02T12:54:54.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload_time = "2025-03-02T12:54:52.069Z" }, + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634, upload-time = "2025-03-02T12:54:52.069Z" }, ] [[package]] @@ -4353,9 +4353,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload_time = "2025-03-25T06:22:28.883Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156, upload-time = "2025-03-25T06:22:28.883Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload_time = "2025-03-25T06:22:27.807Z" }, + { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694, upload-time = "2025-03-25T06:22:27.807Z" }, ] [[package]] @@ -4366,9 +4366,9 @@ dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload_time = "2025-04-05T14:07:51.592Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857, upload-time = "2025-04-05T14:07:51.592Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload_time = "2025-04-05T14:07:49.641Z" }, + { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" }, ] [[package]] @@ -4378,9 +4378,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/a1/c2679d7ff2da20a0f89c7820ae2739cde739eac9b43c192531117b31b5f4/pytest_cpp-2.6.0.tar.gz", hash = "sha256:c2f49d3c038539ac84786a94d852e4f4619c34c95979c2bc69c20b3bdf051d85", size = 465490, upload_time = "2024-09-18T00:08:08.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/a1/c2679d7ff2da20a0f89c7820ae2739cde739eac9b43c192531117b31b5f4/pytest_cpp-2.6.0.tar.gz", hash = "sha256:c2f49d3c038539ac84786a94d852e4f4619c34c95979c2bc69c20b3bdf051d85", size = 465490, upload-time = "2024-09-18T00:08:08.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/44/dc2f5d53165264ae5831f361fe7723c45da05718a97015b2eddc452cf503/pytest_cpp-2.6.0-py3-none-any.whl", hash = "sha256:b33de94609450feea2fba9efff3558b8ac8f1fdf40a99e263b395d4798b911bb", size = 15074, upload_time = "2024-09-18T00:08:06.415Z" }, + { url = "https://files.pythonhosted.org/packages/2a/44/dc2f5d53165264ae5831f361fe7723c45da05718a97015b2eddc452cf503/pytest_cpp-2.6.0-py3-none-any.whl", hash = "sha256:b33de94609450feea2fba9efff3558b8ac8f1fdf40a99e263b395d4798b911bb", size = 15074, upload-time = "2024-09-18T00:08:06.415Z" }, ] [[package]] @@ -4390,9 +4390,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload_time = "2024-03-21T22:14:04.964Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814, upload-time = "2024-03-21T22:14:04.964Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload_time = "2024-03-21T22:14:02.694Z" }, + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863, upload-time = "2024-03-21T22:14:02.694Z" }, ] [[package]] @@ -4402,9 +4402,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload_time = "2024-10-25T15:45:34.274Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload-time = "2024-10-25T15:45:34.274Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload_time = "2024-10-25T15:45:32.78Z" }, + { url = "https://files.pythonhosted.org/packages/22/70/b31577d7c46d8e2f9baccfed5067dd8475262a2331ffb0bfdf19361c9bde/pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6", size = 8396, upload-time = "2024-10-25T15:45:32.78Z" }, ] [[package]] @@ -4414,9 +4414,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/d4/69e9dbb9b8266df0b157c72be32083403c412990af15c7c15f7a3fd1b142/pytest_repeat-0.9.4.tar.gz", hash = "sha256:d92ac14dfaa6ffcfe6917e5d16f0c9bc82380c135b03c2a5f412d2637f224485", size = 6488, upload_time = "2025-04-07T14:59:53.077Z" } +sdist = { url = "https://files.pythonhosted.org/packages/80/d4/69e9dbb9b8266df0b157c72be32083403c412990af15c7c15f7a3fd1b142/pytest_repeat-0.9.4.tar.gz", hash = "sha256:d92ac14dfaa6ffcfe6917e5d16f0c9bc82380c135b03c2a5f412d2637f224485", size = 6488, upload-time = "2025-04-07T14:59:53.077Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/d4/8b706b81b07b43081bd68a2c0359fe895b74bf664b20aca8005d2bb3be71/pytest_repeat-0.9.4-py3-none-any.whl", hash = "sha256:c1738b4e412a6f3b3b9e0b8b29fcd7a423e50f87381ad9307ef6f5a8601139f3", size = 4180, upload_time = "2025-04-07T14:59:51.492Z" }, + { url = "https://files.pythonhosted.org/packages/73/d4/8b706b81b07b43081bd68a2c0359fe895b74bf664b20aca8005d2bb3be71/pytest_repeat-0.9.4-py3-none-any.whl", hash = "sha256:c1738b4e412a6f3b3b9e0b8b29fcd7a423e50f87381ad9307ef6f5a8601139f3", size = 4180, upload-time = "2025-04-07T14:59:51.492Z" }, ] [[package]] @@ -4427,9 +4427,9 @@ dependencies = [ { name = "attrs" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/4c/ba9eab21a2250c2d46c06c0e3cd316850fde9a90da0ac8d0202f074c6817/pytest_subtests-0.14.1.tar.gz", hash = "sha256:350c00adc36c3aff676a66135c81aed9e2182e15f6c3ec8721366918bbbf7580", size = 17632, upload_time = "2024-12-10T00:21:04.856Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/4c/ba9eab21a2250c2d46c06c0e3cd316850fde9a90da0ac8d0202f074c6817/pytest_subtests-0.14.1.tar.gz", hash = "sha256:350c00adc36c3aff676a66135c81aed9e2182e15f6c3ec8721366918bbbf7580", size = 17632, upload-time = "2024-12-10T00:21:04.856Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/b7/7ca948d35642ae72500efda6ba6fa61dcb6683feb596d19c4747c63c0789/pytest_subtests-0.14.1-py3-none-any.whl", hash = "sha256:e92a780d98b43118c28a16044ad9b841727bd7cb6a417073b38fd2d7ccdf052d", size = 8833, upload_time = "2024-12-10T00:20:58.873Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b7/7ca948d35642ae72500efda6ba6fa61dcb6683feb596d19c4747c63c0789/pytest_subtests-0.14.1-py3-none-any.whl", hash = "sha256:e92a780d98b43118c28a16044ad9b841727bd7cb6a417073b38fd2d7ccdf052d", size = 8833, upload-time = "2024-12-10T00:20:58.873Z" }, ] [[package]] @@ -4439,9 +4439,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/0d/04719abc7a4bdb3a7a1f968f24b0f5253d698c9cc94975330e9d3145befb/pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", size = 17697, upload_time = "2024-03-07T21:04:01.069Z" } +sdist = { url = "https://files.pythonhosted.org/packages/93/0d/04719abc7a4bdb3a7a1f968f24b0f5253d698c9cc94975330e9d3145befb/pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", size = 17697, upload-time = "2024-03-07T21:04:01.069Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148, upload_time = "2024-03-07T21:03:58.764Z" }, + { url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148, upload-time = "2024-03-07T21:03:58.764Z" }, ] [[package]] @@ -4452,9 +4452,9 @@ dependencies = [ { name = "execnet" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060, upload_time = "2024-04-28T19:29:54.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060, upload-time = "2024-04-28T19:29:54.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108, upload_time = "2024-04-28T19:29:52.813Z" }, + { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108, upload-time = "2024-04-28T19:29:52.813Z" }, ] [[package]] @@ -4464,9 +4464,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload_time = "2024-03-01T18:36:20.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload_time = "2024-03-01T18:36:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] @@ -4476,16 +4476,16 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload_time = "2022-12-25T18:53:00.824Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload-time = "2022-12-25T18:53:00.824Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185, upload_time = "2022-12-25T18:52:58.662Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398", size = 182185, upload-time = "2022-12-25T18:52:58.662Z" }, ] [[package]] name = "python3-xlib" version = "0.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828, upload_time = "2014-05-31T12:28:59.603Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8", size = 132828, upload-time = "2014-05-31T12:28:59.603Z" } [[package]] name = "pytools" @@ -4496,28 +4496,28 @@ dependencies = [ { name = "siphash24" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload_time = "2024-07-17T18:47:38.287Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/cf/0a6aaa44b1f9e02b8c0648b5665a82246a93bcc75224c167b4fafa25c093/pytools-2024.1.10-py3-none-any.whl", hash = "sha256:9cabb71038048291400e244e2da441a051d86053339bc484e64e58d8ea263f44", size = 88108, upload_time = "2024-07-17T18:47:36.173Z" }, + { url = "https://files.pythonhosted.org/packages/66/cf/0a6aaa44b1f9e02b8c0648b5665a82246a93bcc75224c167b4fafa25c093/pytools-2024.1.10-py3-none-any.whl", hash = "sha256:9cabb71038048291400e244e2da441a051d86053339bc484e64e58d8ea263f44", size = 88108, upload-time = "2024-07-17T18:47:36.173Z" }, ] [[package]] name = "pytweening" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241, upload_time = "2024-02-20T03:37:56.809Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/0c/c16bc93ac2755bac0066a8ecbd2a2931a1735a6fffd99a2b9681c7e83e90/pytweening-1.2.0.tar.gz", hash = "sha256:243318b7736698066c5f362ec5c2b6434ecf4297c3c8e7caa8abfe6af4cac71b", size = 171241, upload-time = "2024-02-20T03:37:56.809Z" } [[package]] name = "pywin32" version = "310" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284, upload_time = "2025-03-17T00:55:53.124Z" }, - { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748, upload_time = "2025-03-17T00:55:55.203Z" }, - { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941, upload_time = "2025-03-17T00:55:57.048Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload_time = "2025-03-17T00:55:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload_time = "2025-03-17T00:56:00.8Z" }, - { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload_time = "2025-03-17T00:56:02.601Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284, upload-time = "2025-03-17T00:55:53.124Z" }, + { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748, upload-time = "2025-03-17T00:55:55.203Z" }, + { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941, upload-time = "2025-03-17T00:55:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload-time = "2025-03-17T00:55:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload-time = "2025-03-17T00:56:00.8Z" }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload-time = "2025-03-17T00:56:02.601Z" }, ] [[package]] @@ -4532,7 +4532,7 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/37/d59397221e15d2a7f38afaa4e8e6b8c244d818044f7daa0bdc5988df0a69/PyWinBox-0.7-py3-none-any.whl", hash = "sha256:8b2506a8dd7afa0a910b368762adfac885274132ef9151b0c81b0d2c6ffd6f83", size = 12274, upload_time = "2024-04-17T10:10:31.899Z" }, + { url = "https://files.pythonhosted.org/packages/b1/37/d59397221e15d2a7f38afaa4e8e6b8c244d818044f7daa0bdc5988df0a69/PyWinBox-0.7-py3-none-any.whl", hash = "sha256:8b2506a8dd7afa0a910b368762adfac885274132ef9151b0c81b0d2c6ffd6f83", size = 12274, upload-time = "2024-04-17T10:10:31.899Z" }, ] [[package]] @@ -4549,33 +4549,33 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/be/33/8e4f632210b28fc9e998a9ab990e7ed97ecd2800cc50038e3800e1d85dbe/PyWinCtl-0.4.1-py3-none-any.whl", hash = "sha256:4d875e22969e1c6239d8c73156193630aaab876366167b8d97716f956384b089", size = 63158, upload_time = "2024-09-23T08:33:39.881Z" }, + { url = "https://files.pythonhosted.org/packages/be/33/8e4f632210b28fc9e998a9ab990e7ed97ecd2800cc50038e3800e1d85dbe/PyWinCtl-0.4.1-py3-none-any.whl", hash = "sha256:4d875e22969e1c6239d8c73156193630aaab876366167b8d97716f956384b089", size = 63158, upload-time = "2024-09-23T08:33:39.881Z" }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload_time = "2024-08-06T20:33:50.674Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload_time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload_time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload_time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload_time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload_time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload_time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload_time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload_time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload_time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload_time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload_time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload_time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload_time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload_time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload_time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload_time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload_time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload_time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, ] [[package]] @@ -4585,9 +4585,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631, upload_time = "2020-11-12T02:38:26.239Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631, upload-time = "2020-11-12T02:38:26.239Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911, upload_time = "2020-11-12T02:38:24.638Z" }, + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911, upload-time = "2020-11-12T02:38:24.638Z" }, ] [[package]] @@ -4597,35 +4597,35 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "implementation_name == 'pypy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293, upload_time = "2025-04-04T12:05:44.049Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293, upload-time = "2025-04-04T12:05:44.049Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723, upload_time = "2025-04-04T12:03:24.358Z" }, - { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645, upload_time = "2025-04-04T12:03:25.693Z" }, - { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133, upload_time = "2025-04-04T12:03:27.625Z" }, - { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428, upload_time = "2025-04-04T12:03:29.004Z" }, - { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409, upload_time = "2025-04-04T12:03:31.032Z" }, - { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007, upload_time = "2025-04-04T12:03:32.687Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599, upload_time = "2025-04-04T12:03:34.084Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546, upload_time = "2025-04-04T12:03:35.478Z" }, - { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247, upload_time = "2025-04-04T12:03:36.846Z" }, - { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727, upload_time = "2025-04-04T12:03:38.578Z" }, - { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942, upload_time = "2025-04-04T12:03:40.143Z" }, - { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586, upload_time = "2025-04-04T12:03:41.954Z" }, - { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880, upload_time = "2025-04-04T12:03:43.45Z" }, - { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216, upload_time = "2025-04-04T12:03:45.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814, upload_time = "2025-04-04T12:03:47.188Z" }, - { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889, upload_time = "2025-04-04T12:03:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153, upload_time = "2025-04-04T12:03:50.591Z" }, - { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352, upload_time = "2025-04-04T12:03:52.473Z" }, - { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834, upload_time = "2025-04-04T12:03:54Z" }, - { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992, upload_time = "2025-04-04T12:03:55.815Z" }, - { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466, upload_time = "2025-04-04T12:03:57.231Z" }, - { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342, upload_time = "2025-04-04T12:03:59.218Z" }, - { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405, upload_time = "2025-04-04T12:05:13.3Z" }, - { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578, upload_time = "2025-04-04T12:05:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248, upload_time = "2025-04-04T12:05:17.376Z" }, - { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757, upload_time = "2025-04-04T12:05:19.19Z" }, - { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371, upload_time = "2025-04-04T12:05:20.702Z" }, + { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723, upload-time = "2025-04-04T12:03:24.358Z" }, + { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645, upload-time = "2025-04-04T12:03:25.693Z" }, + { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133, upload-time = "2025-04-04T12:03:27.625Z" }, + { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428, upload-time = "2025-04-04T12:03:29.004Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409, upload-time = "2025-04-04T12:03:31.032Z" }, + { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007, upload-time = "2025-04-04T12:03:32.687Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599, upload-time = "2025-04-04T12:03:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546, upload-time = "2025-04-04T12:03:35.478Z" }, + { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247, upload-time = "2025-04-04T12:03:36.846Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727, upload-time = "2025-04-04T12:03:38.578Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942, upload-time = "2025-04-04T12:03:40.143Z" }, + { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586, upload-time = "2025-04-04T12:03:41.954Z" }, + { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880, upload-time = "2025-04-04T12:03:43.45Z" }, + { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216, upload-time = "2025-04-04T12:03:45.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814, upload-time = "2025-04-04T12:03:47.188Z" }, + { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889, upload-time = "2025-04-04T12:03:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153, upload-time = "2025-04-04T12:03:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352, upload-time = "2025-04-04T12:03:52.473Z" }, + { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834, upload-time = "2025-04-04T12:03:54Z" }, + { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992, upload-time = "2025-04-04T12:03:55.815Z" }, + { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466, upload-time = "2025-04-04T12:03:57.231Z" }, + { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342, upload-time = "2025-04-04T12:03:59.218Z" }, + { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405, upload-time = "2025-04-04T12:05:13.3Z" }, + { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578, upload-time = "2025-04-04T12:05:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248, upload-time = "2025-04-04T12:05:17.376Z" }, + { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757, upload-time = "2025-04-04T12:05:19.19Z" }, + { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371, upload-time = "2025-04-04T12:05:20.702Z" }, ] [[package]] @@ -4635,21 +4635,21 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/35/9bf3a2af73c55fd4310dcaec4f997c739888e0db9b4dfac71b7680810852/raylib-5.5.0.2.tar.gz", hash = "sha256:83c108ae3b4af40b53c93d1de2afbe309e986dd5efeb280ebe2e61c79956edb0", size = 181172, upload_time = "2024-11-26T11:12:02.791Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/35/9bf3a2af73c55fd4310dcaec4f997c739888e0db9b4dfac71b7680810852/raylib-5.5.0.2.tar.gz", hash = "sha256:83c108ae3b4af40b53c93d1de2afbe309e986dd5efeb280ebe2e61c79956edb0", size = 181172, upload-time = "2024-11-26T11:12:02.791Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c4/ce21721b474eb8f65379f7315b382ccfe1d5df728eea4dcf287b874e7461/raylib-5.5.0.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:37eb0ec97fc6b08f989489a50e09b5dde519e1bb8eb17e4033ac82227b0e5eda", size = 1703742, upload_time = "2024-11-26T11:09:31.115Z" }, - { url = "https://files.pythonhosted.org/packages/23/61/138e305c82549869bb8cd41abe75571559eafbeab6aed1ce7d8fbe3ffd58/raylib-5.5.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bb9e506ecd3dbec6dba868eb036269837a8bde68220690842c3238239ee887ef", size = 1247449, upload_time = "2024-11-26T11:09:34.182Z" }, - { url = "https://files.pythonhosted.org/packages/85/e0/dc638c42d1a505f0992263d48e1434d82c21afdf376b06f549d2e281dfd4/raylib-5.5.0.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:70aa8bed67875a8cf25191f35263ef92d646bdfcb1f507915c81562a321f4931", size = 2184315, upload_time = "2024-11-26T11:09:36.715Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1a/49db57283a28fdc1ff0e4604911b7fff085128c2ac8bdd9efa8c5c47439d/raylib-5.5.0.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:0365e8c578f72f598795d9377fc70342f0d62aa193c2f304ca048b3e28866752", size = 2278139, upload_time = "2024-11-26T11:09:39.475Z" }, - { url = "https://files.pythonhosted.org/packages/f0/8a/e1a690ab6889d4cb67346a2d32bad8b8e8b0f85ec826b00f76b0ad7e6ad6/raylib-5.5.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:5219be70e7fca03e9c4fddebf7e60e885d77137125c7a13f3800a947f8562a13", size = 1693944, upload_time = "2024-11-26T11:09:41.596Z" }, - { url = "https://files.pythonhosted.org/packages/69/2b/49bfa6833ad74ddf318d54ecafe73d535f583531469ecbd5b009d79667d1/raylib-5.5.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5233c529d9a0cfd469d88239c2182e55c5215a7755d83cc3d611148d3b9c9e67", size = 1706157, upload_time = "2024-11-26T11:09:43.6Z" }, - { url = "https://files.pythonhosted.org/packages/58/9c/8a3f4de0c81ad1228bf26410cfe3ecdc73011c59f18e542685ffc92c0120/raylib-5.5.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1f76204ffbc492722b571b12dbdc0dca89b10da76ddf48c12a3968d2db061dff", size = 1248027, upload_time = "2025-01-04T20:21:46.269Z" }, - { url = "https://files.pythonhosted.org/packages/7f/16/63baf1aae94832b9f5d15cafcee67bb6dd07a20cf64d40bac09903b79274/raylib-5.5.0.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:f8cc2e39f1d6b29211a97ec0ac818a5b04c43a40e747e4b4622101d48c711f9e", size = 2195374, upload_time = "2024-11-26T11:09:46.114Z" }, - { url = "https://files.pythonhosted.org/packages/70/bd/61a006b4e3ce4a6ca974cb0ceeb19f3816815ebabac650e9bf82767e65f6/raylib-5.5.0.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f12da578a28da7f48481f46323e5aab8dd25461982b0e80d045782d6e69649f5", size = 2299593, upload_time = "2024-11-26T11:09:48.963Z" }, - { url = "https://files.pythonhosted.org/packages/f4/4f/59d554cc495bea8235b17cebfc76ed57aaa602c613b870159e31282fd4c1/raylib-5.5.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:b40234bbad9523fd6a2049640c76a98b4d6f0b8f4bd19bd33eaee55faf5e050d", size = 1696780, upload_time = "2024-11-26T11:09:50.787Z" }, - { url = "https://files.pythonhosted.org/packages/4a/22/2e02e3738ad041f5ec2830aecdfab411fc2960bfc3400e03b477284bfaf7/raylib-5.5.0.2-pp311-pypy311_pp73-macosx_10_13_x86_64.whl", hash = "sha256:bc45fe1c0aac50aa319a9a66d44bb2bd0dcd038a44d95978191ae7bfeb4a06d8", size = 1216231, upload_time = "2025-02-12T04:21:59.38Z" }, - { url = "https://files.pythonhosted.org/packages/fe/7d/b29afedc4a706b12143f74f322cb32ad5a6f43e56aaca2a9fb89b0d94eee/raylib-5.5.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.whl", hash = "sha256:2242fd6079da5137e9863a447224f800adef6386ca8f59013a5d62cc5cadab2b", size = 1394928, upload_time = "2025-02-12T04:22:03.021Z" }, - { url = "https://files.pythonhosted.org/packages/b6/fa/2daf36d78078c6871b241168a36156169cfc8ea089faba5abe8edad304be/raylib-5.5.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e475a40764c9f83f9e66406bd86d85587eb923329a61ade463c3c59e1e880b16", size = 1564224, upload_time = "2025-02-12T04:22:05.911Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c4/ce21721b474eb8f65379f7315b382ccfe1d5df728eea4dcf287b874e7461/raylib-5.5.0.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:37eb0ec97fc6b08f989489a50e09b5dde519e1bb8eb17e4033ac82227b0e5eda", size = 1703742, upload-time = "2024-11-26T11:09:31.115Z" }, + { url = "https://files.pythonhosted.org/packages/23/61/138e305c82549869bb8cd41abe75571559eafbeab6aed1ce7d8fbe3ffd58/raylib-5.5.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bb9e506ecd3dbec6dba868eb036269837a8bde68220690842c3238239ee887ef", size = 1247449, upload-time = "2024-11-26T11:09:34.182Z" }, + { url = "https://files.pythonhosted.org/packages/85/e0/dc638c42d1a505f0992263d48e1434d82c21afdf376b06f549d2e281dfd4/raylib-5.5.0.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:70aa8bed67875a8cf25191f35263ef92d646bdfcb1f507915c81562a321f4931", size = 2184315, upload-time = "2024-11-26T11:09:36.715Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1a/49db57283a28fdc1ff0e4604911b7fff085128c2ac8bdd9efa8c5c47439d/raylib-5.5.0.2-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:0365e8c578f72f598795d9377fc70342f0d62aa193c2f304ca048b3e28866752", size = 2278139, upload-time = "2024-11-26T11:09:39.475Z" }, + { url = "https://files.pythonhosted.org/packages/f0/8a/e1a690ab6889d4cb67346a2d32bad8b8e8b0f85ec826b00f76b0ad7e6ad6/raylib-5.5.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:5219be70e7fca03e9c4fddebf7e60e885d77137125c7a13f3800a947f8562a13", size = 1693944, upload-time = "2024-11-26T11:09:41.596Z" }, + { url = "https://files.pythonhosted.org/packages/69/2b/49bfa6833ad74ddf318d54ecafe73d535f583531469ecbd5b009d79667d1/raylib-5.5.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5233c529d9a0cfd469d88239c2182e55c5215a7755d83cc3d611148d3b9c9e67", size = 1706157, upload-time = "2024-11-26T11:09:43.6Z" }, + { url = "https://files.pythonhosted.org/packages/58/9c/8a3f4de0c81ad1228bf26410cfe3ecdc73011c59f18e542685ffc92c0120/raylib-5.5.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1f76204ffbc492722b571b12dbdc0dca89b10da76ddf48c12a3968d2db061dff", size = 1248027, upload-time = "2025-01-04T20:21:46.269Z" }, + { url = "https://files.pythonhosted.org/packages/7f/16/63baf1aae94832b9f5d15cafcee67bb6dd07a20cf64d40bac09903b79274/raylib-5.5.0.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:f8cc2e39f1d6b29211a97ec0ac818a5b04c43a40e747e4b4622101d48c711f9e", size = 2195374, upload-time = "2024-11-26T11:09:46.114Z" }, + { url = "https://files.pythonhosted.org/packages/70/bd/61a006b4e3ce4a6ca974cb0ceeb19f3816815ebabac650e9bf82767e65f6/raylib-5.5.0.2-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:f12da578a28da7f48481f46323e5aab8dd25461982b0e80d045782d6e69649f5", size = 2299593, upload-time = "2024-11-26T11:09:48.963Z" }, + { url = "https://files.pythonhosted.org/packages/f4/4f/59d554cc495bea8235b17cebfc76ed57aaa602c613b870159e31282fd4c1/raylib-5.5.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:b40234bbad9523fd6a2049640c76a98b4d6f0b8f4bd19bd33eaee55faf5e050d", size = 1696780, upload-time = "2024-11-26T11:09:50.787Z" }, + { url = "https://files.pythonhosted.org/packages/4a/22/2e02e3738ad041f5ec2830aecdfab411fc2960bfc3400e03b477284bfaf7/raylib-5.5.0.2-pp311-pypy311_pp73-macosx_10_13_x86_64.whl", hash = "sha256:bc45fe1c0aac50aa319a9a66d44bb2bd0dcd038a44d95978191ae7bfeb4a06d8", size = 1216231, upload-time = "2025-02-12T04:21:59.38Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7d/b29afedc4a706b12143f74f322cb32ad5a6f43e56aaca2a9fb89b0d94eee/raylib-5.5.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.whl", hash = "sha256:2242fd6079da5137e9863a447224f800adef6386ca8f59013a5d62cc5cadab2b", size = 1394928, upload-time = "2025-02-12T04:22:03.021Z" }, + { url = "https://files.pythonhosted.org/packages/b6/fa/2daf36d78078c6871b241168a36156169cfc8ea089faba5abe8edad304be/raylib-5.5.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e475a40764c9f83f9e66406bd86d85587eb923329a61ade463c3c59e1e880b16", size = 1564224, upload-time = "2025-02-12T04:22:05.911Z" }, ] [[package]] @@ -4662,9 +4662,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload_time = "2024-05-29T15:37:49.536Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload_time = "2024-05-29T15:37:47.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, ] [[package]] @@ -4679,11 +4679,11 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/6e/a125f4fe2de3269f443b7cb65d465ffd37a836a2dac7e4318e21239d78c8/rerun_sdk-0.23.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fe06d21cfcf4d84a9396f421d4779efabec7e9674d232a2c552c8a91d871c375", size = 66094053, upload_time = "2025-04-25T13:15:48.669Z" }, - { url = "https://files.pythonhosted.org/packages/55/f6/b6d13322b05dc77bd9a0127e98155c2b7ee987a236fd4d331eed2e547a90/rerun_sdk-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:823ae87bfa644e06fb70bada08a83690dd23d9824a013947f80a22c6731bdc0d", size = 62047843, upload_time = "2025-04-25T13:15:54.48Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7f/6a7422cb727e14a65b55b0089988eeea8d0532c429397a863e6ba395554a/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:dc5129f8744f71249bf45558c853422c51ef39b6b5eea0ea1f602c6049ce732f", size = 68214509, upload_time = "2025-04-25T13:15:59.339Z" }, - { url = "https://files.pythonhosted.org/packages/4f/86/3aee9eadbfe55188a2c7d739378545b4319772a4d3b165e8d3fc598fa630/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:ee0d0e17df0e08be13b77cc74884c5d8ba8edb39b6f5a60dc2429d39033d90f6", size = 71442196, upload_time = "2025-04-25T13:16:04.405Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ba/028bd382e2ae21e6643cec25f423285dbc6b328ce56d55727b4101ef9443/rerun_sdk-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:d4273db55b56310b053a2de6bf5927a8692cf65f4d234c6e6928fb24ed8a960d", size = 57583198, upload_time = "2025-04-25T13:16:08.905Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6e/a125f4fe2de3269f443b7cb65d465ffd37a836a2dac7e4318e21239d78c8/rerun_sdk-0.23.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fe06d21cfcf4d84a9396f421d4779efabec7e9674d232a2c552c8a91d871c375", size = 66094053, upload-time = "2025-04-25T13:15:48.669Z" }, + { url = "https://files.pythonhosted.org/packages/55/f6/b6d13322b05dc77bd9a0127e98155c2b7ee987a236fd4d331eed2e547a90/rerun_sdk-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:823ae87bfa644e06fb70bada08a83690dd23d9824a013947f80a22c6731bdc0d", size = 62047843, upload-time = "2025-04-25T13:15:54.48Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7f/6a7422cb727e14a65b55b0089988eeea8d0532c429397a863e6ba395554a/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:dc5129f8744f71249bf45558c853422c51ef39b6b5eea0ea1f602c6049ce732f", size = 68214509, upload-time = "2025-04-25T13:15:59.339Z" }, + { url = "https://files.pythonhosted.org/packages/4f/86/3aee9eadbfe55188a2c7d739378545b4319772a4d3b165e8d3fc598fa630/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:ee0d0e17df0e08be13b77cc74884c5d8ba8edb39b6f5a60dc2429d39033d90f6", size = 71442196, upload-time = "2025-04-25T13:16:04.405Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ba/028bd382e2ae21e6643cec25f423285dbc6b328ce56d55727b4101ef9443/rerun_sdk-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:d4273db55b56310b053a2de6bf5927a8692cf65f4d234c6e6928fb24ed8a960d", size = 57583198, upload-time = "2025-04-25T13:16:08.905Z" }, ] [[package]] @@ -4693,78 +4693,78 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ruamel-yaml-clib", marker = "platform_python_implementation == 'CPython'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447, upload_time = "2025-01-06T14:08:51.334Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/46/f44d8be06b85bc7c4d8c95d658be2b68f27711f279bf9dd0612a5e4794f5/ruamel.yaml-0.18.10.tar.gz", hash = "sha256:20c86ab29ac2153f80a428e1254a8adf686d3383df04490514ca3b79a362db58", size = 143447, upload-time = "2025-01-06T14:08:51.334Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729, upload_time = "2025-01-06T14:08:47.471Z" }, + { url = "https://files.pythonhosted.org/packages/c2/36/dfc1ebc0081e6d39924a2cc53654497f967a084a436bb64402dfce4254d9/ruamel.yaml-0.18.10-py3-none-any.whl", hash = "sha256:30f22513ab2301b3d2b577adc121c6471f28734d3d9728581245f1e76468b4f1", size = 117729, upload-time = "2025-01-06T14:08:47.471Z" }, ] [[package]] name = "ruamel-yaml-clib" version = "0.2.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload_time = "2024-10-20T10:10:56.22Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/84/80203abff8ea4993a87d823a5f632e4d92831ef75d404c9fc78d0176d2b5/ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f", size = 225315, upload-time = "2024-10-20T10:10:56.22Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload_time = "2024-10-20T10:12:45.162Z" }, - { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload_time = "2024-10-20T10:12:46.758Z" }, - { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload_time = "2024-10-20T10:12:48.605Z" }, - { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload_time = "2024-10-20T10:12:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload_time = "2024-10-21T11:26:41.438Z" }, - { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload_time = "2024-10-21T11:26:43.62Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload_time = "2024-12-11T19:58:15.592Z" }, - { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload_time = "2024-10-20T10:12:52.865Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload_time = "2024-10-20T10:12:54.652Z" }, - { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload_time = "2024-10-20T10:12:55.657Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload_time = "2024-10-20T10:12:57.155Z" }, - { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload_time = "2024-10-20T10:12:58.501Z" }, - { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload_time = "2024-10-20T10:13:00.211Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload_time = "2024-10-21T11:26:46.038Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload_time = "2024-10-21T11:26:47.487Z" }, - { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload_time = "2024-12-11T19:58:17.252Z" }, - { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload_time = "2024-10-20T10:13:01.395Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload_time = "2024-10-20T10:13:02.768Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8f/683c6ad562f558cbc4f7c029abcd9599148c51c54b5ef0f24f2638da9fbb/ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6", size = 132224, upload-time = "2024-10-20T10:12:45.162Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d2/b79b7d695e2f21da020bd44c782490578f300dd44f0a4c57a92575758a76/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e", size = 641480, upload-time = "2024-10-20T10:12:46.758Z" }, + { url = "https://files.pythonhosted.org/packages/68/6e/264c50ce2a31473a9fdbf4fa66ca9b2b17c7455b31ef585462343818bd6c/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e", size = 739068, upload-time = "2024-10-20T10:12:48.605Z" }, + { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012, upload-time = "2024-10-20T10:12:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352, upload-time = "2024-10-21T11:26:41.438Z" }, + { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344, upload-time = "2024-10-21T11:26:43.62Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498, upload-time = "2024-12-11T19:58:15.592Z" }, + { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205, upload-time = "2024-10-20T10:12:52.865Z" }, + { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185, upload-time = "2024-10-20T10:12:54.652Z" }, + { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433, upload-time = "2024-10-20T10:12:55.657Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b0/b850385604334c2ce90e3ee1013bd911aedf058a934905863a6ea95e9eb4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d", size = 647362, upload-time = "2024-10-20T10:12:57.155Z" }, + { url = "https://files.pythonhosted.org/packages/44/d0/3f68a86e006448fb6c005aee66565b9eb89014a70c491d70c08de597f8e4/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c", size = 754118, upload-time = "2024-10-20T10:12:58.501Z" }, + { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497, upload-time = "2024-10-20T10:13:00.211Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042, upload-time = "2024-10-21T11:26:46.038Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831, upload-time = "2024-10-21T11:26:47.487Z" }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692, upload-time = "2024-12-11T19:58:17.252Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777, upload-time = "2024-10-20T10:13:01.395Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523, upload-time = "2024-10-20T10:13:02.768Z" }, ] [[package]] name = "rubicon-objc" version = "0.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/13/586c9baa985eae0f718029506b40ca41295d51a546567414b2bcf8ccacef/rubicon_objc-0.5.0.tar.gz", hash = "sha256:18f075649780d95df53d483642068c767d7d2cfbbf075ddef124a44b40b6d92e", size = 173652, upload_time = "2025-01-07T00:25:10.491Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/13/586c9baa985eae0f718029506b40ca41295d51a546567414b2bcf8ccacef/rubicon_objc-0.5.0.tar.gz", hash = "sha256:18f075649780d95df53d483642068c767d7d2cfbbf075ddef124a44b40b6d92e", size = 173652, upload-time = "2025-01-07T00:25:10.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/30/5b2407b8762ed882e5732e19c485b9ea2f07d35462615a3212638bab66c2/rubicon_objc-0.5.0-py3-none-any.whl", hash = "sha256:a9c2a605120d6e5be327d3f42a71b60963125987e116f51846757b5e110854fa", size = 62711, upload_time = "2025-01-07T00:25:08.959Z" }, + { url = "https://files.pythonhosted.org/packages/6d/30/5b2407b8762ed882e5732e19c485b9ea2f07d35462615a3212638bab66c2/rubicon_objc-0.5.0-py3-none-any.whl", hash = "sha256:a9c2a605120d6e5be327d3f42a71b60963125987e116f51846757b5e110854fa", size = 62711, upload-time = "2025-01-07T00:25:08.959Z" }, ] [[package]] name = "ruff" -version = "0.11.7" +version = "0.11.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5b/89/6f9c9674818ac2e9cc2f2b35b704b7768656e6b7c139064fc7ba8fbc99f1/ruff-0.11.7.tar.gz", hash = "sha256:655089ad3224070736dc32844fde783454f8558e71f501cb207485fe4eee23d4", size = 4054861, upload_time = "2025-04-24T18:49:37.007Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f6/adcf73711f31c9f5393862b4281c875a462d9f639f4ccdf69dc368311c20/ruff-0.11.8.tar.gz", hash = "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8", size = 4086399, upload-time = "2025-05-01T14:53:24.459Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/ec/21927cb906c5614b786d1621dba405e3d44f6e473872e6df5d1a6bca0455/ruff-0.11.7-py3-none-linux_armv6l.whl", hash = "sha256:d29e909d9a8d02f928d72ab7837b5cbc450a5bdf578ab9ebee3263d0a525091c", size = 10245403, upload_time = "2025-04-24T18:48:40.459Z" }, - { url = "https://files.pythonhosted.org/packages/e2/af/fec85b6c2c725bcb062a354dd7cbc1eed53c33ff3aa665165871c9c16ddf/ruff-0.11.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dd1fb86b168ae349fb01dd497d83537b2c5541fe0626e70c786427dd8363aaee", size = 11007166, upload_time = "2025-04-24T18:48:44.742Z" }, - { url = "https://files.pythonhosted.org/packages/31/9a/2d0d260a58e81f388800343a45898fd8df73c608b8261c370058b675319a/ruff-0.11.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d3d7d2e140a6fbbc09033bce65bd7ea29d6a0adeb90b8430262fbacd58c38ada", size = 10378076, upload_time = "2025-04-24T18:48:47.918Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/9b09b45051404d2e7dd6d9dbcbabaa5ab0093f9febcae664876a77b9ad53/ruff-0.11.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4809df77de390a1c2077d9b7945d82f44b95d19ceccf0c287c56e4dc9b91ca64", size = 10557138, upload_time = "2025-04-24T18:48:51.707Z" }, - { url = "https://files.pythonhosted.org/packages/5e/5e/f62a1b6669870a591ed7db771c332fabb30f83c967f376b05e7c91bccd14/ruff-0.11.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3a0c2e169e6b545f8e2dba185eabbd9db4f08880032e75aa0e285a6d3f48201", size = 10095726, upload_time = "2025-04-24T18:48:54.243Z" }, - { url = "https://files.pythonhosted.org/packages/45/59/a7aa8e716f4cbe07c3500a391e58c52caf665bb242bf8be42c62adef649c/ruff-0.11.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49b888200a320dd96a68e86736cf531d6afba03e4f6cf098401406a257fcf3d6", size = 11672265, upload_time = "2025-04-24T18:48:57.639Z" }, - { url = "https://files.pythonhosted.org/packages/dd/e3/101a8b707481f37aca5f0fcc3e42932fa38b51add87bfbd8e41ab14adb24/ruff-0.11.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2b19cdb9cf7dae00d5ee2e7c013540cdc3b31c4f281f1dacb5a799d610e90db4", size = 12331418, upload_time = "2025-04-24T18:49:00.697Z" }, - { url = "https://files.pythonhosted.org/packages/dd/71/037f76cbe712f5cbc7b852e4916cd3cf32301a30351818d32ab71580d1c0/ruff-0.11.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64e0ee994c9e326b43539d133a36a455dbaab477bc84fe7bfbd528abe2f05c1e", size = 11794506, upload_time = "2025-04-24T18:49:03.545Z" }, - { url = "https://files.pythonhosted.org/packages/ca/de/e450b6bab1fc60ef263ef8fcda077fb4977601184877dce1c59109356084/ruff-0.11.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bad82052311479a5865f52c76ecee5d468a58ba44fb23ee15079f17dd4c8fd63", size = 13939084, upload_time = "2025-04-24T18:49:07.159Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2c/1e364cc92970075d7d04c69c928430b23e43a433f044474f57e425cbed37/ruff-0.11.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7940665e74e7b65d427b82bffc1e46710ec7f30d58b4b2d5016e3f0321436502", size = 11450441, upload_time = "2025-04-24T18:49:11.41Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7d/1b048eb460517ff9accd78bca0fa6ae61df2b276010538e586f834f5e402/ruff-0.11.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:169027e31c52c0e36c44ae9a9c7db35e505fee0b39f8d9fca7274a6305295a92", size = 10441060, upload_time = "2025-04-24T18:49:14.184Z" }, - { url = "https://files.pythonhosted.org/packages/3a/57/8dc6ccfd8380e5ca3d13ff7591e8ba46a3b330323515a4996b991b10bd5d/ruff-0.11.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:305b93f9798aee582e91e34437810439acb28b5fc1fee6b8205c78c806845a94", size = 10058689, upload_time = "2025-04-24T18:49:17.559Z" }, - { url = "https://files.pythonhosted.org/packages/23/bf/20487561ed72654147817885559ba2aa705272d8b5dee7654d3ef2dbf912/ruff-0.11.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a681db041ef55550c371f9cd52a3cf17a0da4c75d6bd691092dfc38170ebc4b6", size = 11073703, upload_time = "2025-04-24T18:49:20.247Z" }, - { url = "https://files.pythonhosted.org/packages/9d/27/04f2db95f4ef73dccedd0c21daf9991cc3b7f29901a4362057b132075aa4/ruff-0.11.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:07f1496ad00a4a139f4de220b0c97da6d4c85e0e4aa9b2624167b7d4d44fd6b6", size = 11532822, upload_time = "2025-04-24T18:49:23.765Z" }, - { url = "https://files.pythonhosted.org/packages/e1/72/43b123e4db52144c8add336581de52185097545981ff6e9e58a21861c250/ruff-0.11.7-py3-none-win32.whl", hash = "sha256:f25dfb853ad217e6e5f1924ae8a5b3f6709051a13e9dad18690de6c8ff299e26", size = 10362436, upload_time = "2025-04-24T18:49:27.377Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a0/3e58cd76fdee53d5c8ce7a56d84540833f924ccdf2c7d657cb009e604d82/ruff-0.11.7-py3-none-win_amd64.whl", hash = "sha256:0a931d85959ceb77e92aea4bbedfded0a31534ce191252721128f77e5ae1f98a", size = 11566676, upload_time = "2025-04-24T18:49:30.938Z" }, - { url = "https://files.pythonhosted.org/packages/68/ca/69d7c7752bce162d1516e5592b1cc6b6668e9328c0d270609ddbeeadd7cf/ruff-0.11.7-py3-none-win_arm64.whl", hash = "sha256:778c1e5d6f9e91034142dfd06110534ca13220bfaad5c3735f6cb844654f6177", size = 10677936, upload_time = "2025-04-24T18:49:34.392Z" }, + { url = "https://files.pythonhosted.org/packages/9f/60/c6aa9062fa518a9f86cb0b85248245cddcd892a125ca00441df77d79ef88/ruff-0.11.8-py3-none-linux_armv6l.whl", hash = "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3", size = 10272473, upload-time = "2025-05-01T14:52:37.252Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e4/0325e50d106dc87c00695f7bcd5044c6d252ed5120ebf423773e00270f50/ruff-0.11.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835", size = 11040862, upload-time = "2025-05-01T14:52:41.022Z" }, + { url = "https://files.pythonhosted.org/packages/e6/27/b87ea1a7be37fef0adbc7fd987abbf90b6607d96aa3fc67e2c5b858e1e53/ruff-0.11.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c", size = 10385273, upload-time = "2025-05-01T14:52:43.551Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f7/3346161570d789045ed47a86110183f6ac3af0e94e7fd682772d89f7f1a1/ruff-0.11.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c", size = 10578330, upload-time = "2025-05-01T14:52:45.48Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c3/327fb950b4763c7b3784f91d3038ef10c13b2d42322d4ade5ce13a2f9edb/ruff-0.11.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219", size = 10122223, upload-time = "2025-05-01T14:52:47.675Z" }, + { url = "https://files.pythonhosted.org/packages/de/c7/ba686bce9adfeb6c61cb1bbadc17d58110fe1d602f199d79d4c880170f19/ruff-0.11.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f", size = 11697353, upload-time = "2025-05-01T14:52:50.264Z" }, + { url = "https://files.pythonhosted.org/packages/53/8e/a4fb4a1ddde3c59e73996bb3ac51844ff93384d533629434b1def7a336b0/ruff-0.11.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474", size = 12375936, upload-time = "2025-05-01T14:52:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/ad/a1/9529cb1e2936e2479a51aeb011307e7229225df9ac64ae064d91ead54571/ruff-0.11.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38", size = 11850083, upload-time = "2025-05-01T14:52:55.424Z" }, + { url = "https://files.pythonhosted.org/packages/3e/94/8f7eac4c612673ae15a4ad2bc0ee62e03c68a2d4f458daae3de0e47c67ba/ruff-0.11.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458", size = 14005834, upload-time = "2025-05-01T14:52:58.056Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7c/6f63b46b2be870cbf3f54c9c4154d13fac4b8827f22fa05ac835c10835b2/ruff-0.11.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5", size = 11503713, upload-time = "2025-05-01T14:53:01.244Z" }, + { url = "https://files.pythonhosted.org/packages/3a/91/57de411b544b5fe072779678986a021d87c3ee5b89551f2ca41200c5d643/ruff-0.11.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948", size = 10457182, upload-time = "2025-05-01T14:53:03.726Z" }, + { url = "https://files.pythonhosted.org/packages/01/49/cfe73e0ce5ecdd3e6f1137bf1f1be03dcc819d1bfe5cff33deb40c5926db/ruff-0.11.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb", size = 10101027, upload-time = "2025-05-01T14:53:06.555Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/a5cfe47c62b3531675795f38a0ef1c52ff8de62eaddf370d46634391a3fb/ruff-0.11.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c", size = 11111298, upload-time = "2025-05-01T14:53:08.825Z" }, + { url = "https://files.pythonhosted.org/packages/36/98/f76225f87e88f7cb669ae92c062b11c0a1e91f32705f829bd426f8e48b7b/ruff-0.11.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304", size = 11566884, upload-time = "2025-05-01T14:53:11.626Z" }, + { url = "https://files.pythonhosted.org/packages/de/7e/fff70b02e57852fda17bd43f99dda37b9bcf3e1af3d97c5834ff48d04715/ruff-0.11.8-py3-none-win32.whl", hash = "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2", size = 10451102, upload-time = "2025-05-01T14:53:14.303Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/eaa571eb70648c9bde3120a1d5892597de57766e376b831b06e7c1e43945/ruff-0.11.8-py3-none-win_amd64.whl", hash = "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4", size = 11597410, upload-time = "2025-05-01T14:53:16.571Z" }, + { url = "https://files.pythonhosted.org/packages/cd/be/f6b790d6ae98f1f32c645f8540d5c96248b72343b0a56fab3a07f2941897/ruff-0.11.8-py3-none-win_arm64.whl", hash = "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2", size = 10713129, upload-time = "2025-05-01T14:53:22.27Z" }, ] [[package]] name = "scons" version = "4.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/c1/30176c76c1ef723fab62e5cdb15d3c972427a146cb6f868748613d7b25af/scons-4.9.1.tar.gz", hash = "sha256:bacac880ba2e86d6a156c116e2f8f2bfa82b257046f3ac2666c85c53c615c338", size = 3252106, upload_time = "2025-03-27T20:42:19.765Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/c1/30176c76c1ef723fab62e5cdb15d3c972427a146cb6f868748613d7b25af/scons-4.9.1.tar.gz", hash = "sha256:bacac880ba2e86d6a156c116e2f8f2bfa82b257046f3ac2666c85c53c615c338", size = 3252106, upload-time = "2025-03-27T20:42:19.765Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/92/50b739021983b131dcacd57aa8b04d31c5acc2e7e0eb4ed4a362f438c6b7/scons-4.9.1-py3-none-any.whl", hash = "sha256:d2db1a22eb6039e97cbbb0106f18f435af033d0aad190299c9688378e2810a5e", size = 4131331, upload_time = "2025-03-27T20:42:16.665Z" }, + { url = "https://files.pythonhosted.org/packages/45/92/50b739021983b131dcacd57aa8b04d31c5acc2e7e0eb4ed4a362f438c6b7/scons-4.9.1-py3-none-any.whl", hash = "sha256:d2db1a22eb6039e97cbbb0106f18f435af033d0aad190299c9688378e2810a5e", size = 4131331, upload-time = "2025-03-27T20:42:16.665Z" }, ] [[package]] @@ -4775,50 +4775,50 @@ dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556, upload_time = "2025-04-24T10:09:37.927Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556, upload-time = "2025-04-24T10:09:37.927Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786, upload_time = "2025-04-24T10:09:35.897Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786, upload-time = "2025-04-24T10:09:35.897Z" }, ] [[package]] name = "setproctitle" -version = "1.3.5" +version = "1.3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c4/4d/6a840c8d2baa07b57329490e7094f90aac177a1d5226bc919046f1106860/setproctitle-1.3.5.tar.gz", hash = "sha256:1e6eaeaf8a734d428a95d8c104643b39af7d247d604f40a7bebcf3960a853c5e", size = 26737, upload_time = "2025-02-22T21:52:43.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/af/56efe21c53ac81ac87e000b15e60b3d8104224b4313b6eacac3597bd183d/setproctitle-1.3.6.tar.gz", hash = "sha256:c9f32b96c700bb384f33f7cf07954bb609d35dd82752cef57fb2ee0968409169", size = 26889, upload-time = "2025-04-29T13:35:00.184Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/4a/9e0243c5df221102fb834a947f5753d9da06ad5f84e36b0e2e93f7865edb/setproctitle-1.3.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1c8dcc250872385f2780a5ea58050b58cbc8b6a7e8444952a5a65c359886c593", size = 17256, upload_time = "2025-02-22T21:50:45.928Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a1/76ad2ba6f5bd00609238e3d64eeded4598e742a5f25b5cc1a0efdae5f674/setproctitle-1.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ca82fae9eb4800231dd20229f06e8919787135a5581da245b8b05e864f34cc8b", size = 11893, upload_time = "2025-02-22T21:50:47.167Z" }, - { url = "https://files.pythonhosted.org/packages/47/3a/75d11fedff5b21ba9a4c5fe3dfa5e596f831d094ef1896713a72e9e38833/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0424e1d33232322541cb36fb279ea5242203cd6f20de7b4fb2a11973d8e8c2ce", size = 31631, upload_time = "2025-02-22T21:50:50.863Z" }, - { url = "https://files.pythonhosted.org/packages/5a/12/58220de5600e0ed2e5562297173187d863db49babb03491ffe9c101299bc/setproctitle-1.3.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fec8340ab543144d04a9d805d80a0aad73fdeb54bea6ff94e70d39a676ea4ec0", size = 32975, upload_time = "2025-02-22T21:50:52.188Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c4/fbb308680d83c1c7aa626950308318c6e6381a8273779163a31741f3c752/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eab441c89f181271ab749077dcc94045a423e51f2fb0b120a1463ef9820a08d0", size = 30126, upload_time = "2025-02-22T21:50:53.496Z" }, - { url = "https://files.pythonhosted.org/packages/31/6e/baaf70bd9a881dd8c12cbccdd7ca0ff291024a37044a8245e942e12e7135/setproctitle-1.3.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c371550a2288901a0dcd84192691ebd3197a43c95f3e0b396ed6d1cedf5c6c", size = 31135, upload_time = "2025-02-22T21:50:54.931Z" }, - { url = "https://files.pythonhosted.org/packages/a6/dc/d8ab6b1c3d844dc14f596e3cce76604570848f8a67ba6a3812775ed2c015/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78288ff5f9c415c56595b2257ad218936dd9fa726b36341b373b31ca958590fe", size = 30874, upload_time = "2025-02-22T21:50:57.042Z" }, - { url = "https://files.pythonhosted.org/packages/d4/84/62a359b3aa51228bd88f78b44ebb0256a5b96dd2487881c1e984a59b617d/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f1f13a25fc46731acab518602bb1149bfd8b5fabedf8290a7c0926d61414769d", size = 29893, upload_time = "2025-02-22T21:50:59.644Z" }, - { url = "https://files.pythonhosted.org/packages/e2/d6/b3c52c03ee41e7f006e1a737e0db1c58d1dc28e258b83548e653d0c34f1c/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1534d6cd3854d035e40bf4c091984cbdd4d555d7579676d406c53c8f187c006f", size = 32293, upload_time = "2025-02-22T21:51:01.777Z" }, - { url = "https://files.pythonhosted.org/packages/55/09/c0ba311879d9c05860503a7e2708ace85913b9a816786402a92c664fe930/setproctitle-1.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62a01c76708daac78b9688ffb95268c57cb57fa90b543043cda01358912fe2db", size = 30247, upload_time = "2025-02-22T21:51:03.779Z" }, - { url = "https://files.pythonhosted.org/packages/9e/43/cc7155461f0b5a48aebdb87d78239ff3a51ebda0905de478d9fa6ab92d9c/setproctitle-1.3.5-cp311-cp311-win32.whl", hash = "sha256:ea07f29735d839eaed985990a0ec42c8aecefe8050da89fec35533d146a7826d", size = 11476, upload_time = "2025-02-22T21:51:05.746Z" }, - { url = "https://files.pythonhosted.org/packages/e7/57/6e937ac7aa52db69225f02db2cfdcb66ba1db6fdc65a4ddbdf78e214f72a/setproctitle-1.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab3ae11e10d13d514d4a5a15b4f619341142ba3e18da48c40e8614c5a1b5e3c3", size = 12189, upload_time = "2025-02-22T21:51:07.837Z" }, - { url = "https://files.pythonhosted.org/packages/2b/19/04755958495de57e4891de50f03e77b3fe9ca6716a86de00faa00ad0ee5a/setproctitle-1.3.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:523424b9be4dea97d95b8a584b183f35c7bab2d0a3d995b01febf5b8a8de90e4", size = 17250, upload_time = "2025-02-22T21:51:09.785Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3d/2ca9df5aa49b975296411dcbbe272cdb1c5e514c43b8be7d61751bb71a46/setproctitle-1.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b6ec1d86c1b4d7b5f2bdceadf213310cf24696b82480a2a702194b8a0bfbcb47", size = 11878, upload_time = "2025-02-22T21:51:11.679Z" }, - { url = "https://files.pythonhosted.org/packages/36/d6/e90e23b4627e016a4f862d4f892be92c9765dd6bf1e27a48e52cd166d4a3/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea6c505264275a43e9b2acd2acfc11ac33caf52bc3167c9fced4418a810f6b1c", size = 31940, upload_time = "2025-02-22T21:51:12.977Z" }, - { url = "https://files.pythonhosted.org/packages/15/13/167cdd55e00a8e10b36aad79646c3bf3c23fba0c08a9b8db9b74622c1b13/setproctitle-1.3.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b91e68e6685998e6353f296100ecabc313a6cb3e413d66a03d74b988b61f5ff", size = 33370, upload_time = "2025-02-22T21:51:15.115Z" }, - { url = "https://files.pythonhosted.org/packages/9b/22/574a110527df133409a75053b7d6ff740993ccf30b8713d042f26840d351/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc1fda208ae3a2285ad27aeab44c41daf2328abe58fa3270157a739866779199", size = 30628, upload_time = "2025-02-22T21:51:16.324Z" }, - { url = "https://files.pythonhosted.org/packages/52/79/78b05c7d792c9167b917acdab1773b1ff73b016560f45d8155be2baa1a82/setproctitle-1.3.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:828727d220e46f048b82289018300a64547b46aaed96bf8810c05fe105426b41", size = 31672, upload_time = "2025-02-22T21:51:17.791Z" }, - { url = "https://files.pythonhosted.org/packages/b0/62/4509735be062129694751ac55d5e1fbb6d86fa46a8689b7d5e2c23dae5b0/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:83b016221cf80028b2947be20630faa14e3e72a403e35f0ba29550b4e856767b", size = 31378, upload_time = "2025-02-22T21:51:19.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/e7/b394c55934b89f00c2ef7d5e6f18cca5d8dfa26ef628700c4de0c85e3f3d/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6d8a411e752e794d052434139ca4234ffeceeb8d8d8ddc390a9051d7942b2726", size = 30370, upload_time = "2025-02-22T21:51:21.218Z" }, - { url = "https://files.pythonhosted.org/packages/13/ee/e1f27bf52d2bec7060bb6311ab0ccede8de98ed5394e3a59e7a14a453fb5/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50cfbf86b9c63a2c2903f1231f0a58edeb775e651ae1af84eec8430b0571f29b", size = 32875, upload_time = "2025-02-22T21:51:22.505Z" }, - { url = "https://files.pythonhosted.org/packages/6e/08/13b561085d2de53b9becfa5578545d99114e9ff2aa3dc151bcaadf80b17e/setproctitle-1.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3b5e2eacd572444770026c9dd3ddc7543ce427cdf452d40a408d1e95beefb30", size = 30903, upload_time = "2025-02-22T21:51:23.732Z" }, - { url = "https://files.pythonhosted.org/packages/65/f0/6cd06fffff2553be7b0571447d0c0ef8b727ef44cc2d6a33452677a311c8/setproctitle-1.3.5-cp312-cp312-win32.whl", hash = "sha256:cf4e3ded98027de2596c6cc5bbd3302adfb3ca315c848f56516bb0b7e88de1e9", size = 11468, upload_time = "2025-02-22T21:51:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c1/8c/e8a7cb568c4552618838941b332203bfc77ab0f2d67c1cb8f24dee0370ec/setproctitle-1.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:f7a8c01ffd013dda2bed6e7d5cb59fbb609e72f805abf3ee98360f38f7758d9b", size = 12190, upload_time = "2025-02-22T21:51:26.78Z" }, + { url = "https://files.pythonhosted.org/packages/27/3b/8288d0cd969a63500dd62fc2c99ce6980f9909ccef0770ab1f86c361e0bf/setproctitle-1.3.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a1d856b0f4e4a33e31cdab5f50d0a14998f3a2d726a3fd5cb7c4d45a57b28d1b", size = 17412, upload-time = "2025-04-29T13:32:58.135Z" }, + { url = "https://files.pythonhosted.org/packages/39/37/43a5a3e25ca1048dbbf4db0d88d346226f5f1acd131bb8e660f4bfe2799f/setproctitle-1.3.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:50706b9c0eda55f7de18695bfeead5f28b58aa42fd5219b3b1692d554ecbc9ec", size = 11963, upload-time = "2025-04-29T13:32:59.17Z" }, + { url = "https://files.pythonhosted.org/packages/5b/47/f103c40e133154783c91a10ab08ac9fc410ed835aa85bcf7107cb882f505/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af188f3305f0a65c3217c30c6d4c06891e79144076a91e8b454f14256acc7279", size = 31718, upload-time = "2025-04-29T13:33:00.36Z" }, + { url = "https://files.pythonhosted.org/packages/1f/13/7325dd1c008dd6c0ebd370ddb7505977054a87e406f142318e395031a792/setproctitle-1.3.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce0ed8b3f64c71c140f0ec244e5fdf8ecf78ddf8d2e591d4a8b6aa1c1214235", size = 33027, upload-time = "2025-04-29T13:33:01.499Z" }, + { url = "https://files.pythonhosted.org/packages/0c/0a/6075bfea05a71379d77af98a9ac61163e8b6e5ef1ae58cd2b05871b2079c/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70100e2087fe05359f249a0b5f393127b3a1819bf34dec3a3e0d4941138650c9", size = 30223, upload-time = "2025-04-29T13:33:03.259Z" }, + { url = "https://files.pythonhosted.org/packages/cc/41/fbf57ec52f4f0776193bd94334a841f0bc9d17e745f89c7790f336420c65/setproctitle-1.3.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1065ed36bd03a3fd4186d6c6de5f19846650b015789f72e2dea2d77be99bdca1", size = 31204, upload-time = "2025-04-29T13:33:04.455Z" }, + { url = "https://files.pythonhosted.org/packages/97/b5/f799fb7a00de29fb0ac1dfd015528dea425b9e31a8f1068a0b3df52d317f/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4adf6a0013fe4e0844e3ba7583ec203ca518b9394c6cc0d3354df2bf31d1c034", size = 31181, upload-time = "2025-04-29T13:33:05.697Z" }, + { url = "https://files.pythonhosted.org/packages/b5/b7/81f101b612014ec61723436022c31146178813d6ca6b947f7b9c84e9daf4/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb7452849f6615871eabed6560ffedfe56bc8af31a823b6be4ce1e6ff0ab72c5", size = 30101, upload-time = "2025-04-29T13:33:07.223Z" }, + { url = "https://files.pythonhosted.org/packages/67/23/681232eed7640eab96719daa8647cc99b639e3daff5c287bd270ef179a73/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a094b7ce455ca341b59a0f6ce6be2e11411ba6e2860b9aa3dbb37468f23338f4", size = 32438, upload-time = "2025-04-29T13:33:08.538Z" }, + { url = "https://files.pythonhosted.org/packages/19/f8/4d075a7bdc3609ac71535b849775812455e4c40aedfbf0778a6f123b1774/setproctitle-1.3.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ad1c2c2baaba62823a7f348f469a967ece0062140ca39e7a48e4bbb1f20d54c4", size = 30625, upload-time = "2025-04-29T13:33:09.707Z" }, + { url = "https://files.pythonhosted.org/packages/5f/73/a2a8259ebee166aee1ca53eead75de0e190b3ddca4f716e5c7470ebb7ef6/setproctitle-1.3.6-cp311-cp311-win32.whl", hash = "sha256:8050c01331135f77ec99d99307bfbc6519ea24d2f92964b06f3222a804a3ff1f", size = 11488, upload-time = "2025-04-29T13:33:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/c9/15/52cf5e1ff0727d53704cfdde2858eaf237ce523b0b04db65faa84ff83e13/setproctitle-1.3.6-cp311-cp311-win_amd64.whl", hash = "sha256:9b73cf0fe28009a04a35bb2522e4c5b5176cc148919431dcb73fdbdfaab15781", size = 12201, upload-time = "2025-04-29T13:33:12.389Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fb/99456fd94d4207c5f6c40746a048a33a52b4239cd7d9c8d4889e2210ec82/setproctitle-1.3.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:af44bb7a1af163806bbb679eb8432fa7b4fb6d83a5d403b541b675dcd3798638", size = 17399, upload-time = "2025-04-29T13:33:13.406Z" }, + { url = "https://files.pythonhosted.org/packages/d5/48/9699191fe6062827683c43bfa9caac33a2c89f8781dd8c7253fa3dba85fd/setproctitle-1.3.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cca16fd055316a48f0debfcbfb6af7cea715429fc31515ab3fcac05abd527d8", size = 11966, upload-time = "2025-04-29T13:33:14.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/03/b085d192b9ecb9c7ce6ad6ef30ecf4110b7f39430b58a56245569827fcf4/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea002088d5554fd75e619742cefc78b84a212ba21632e59931b3501f0cfc8f67", size = 32017, upload-time = "2025-04-29T13:33:16.163Z" }, + { url = "https://files.pythonhosted.org/packages/ae/68/c53162e645816f97212002111420d1b2f75bf6d02632e37e961dc2cd6d8b/setproctitle-1.3.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb465dd5825356c1191a038a86ee1b8166e3562d6e8add95eec04ab484cfb8a2", size = 33419, upload-time = "2025-04-29T13:33:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0d/119a45d15a816a6cf5ccc61b19729f82620095b27a47e0a6838216a95fae/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2c8e20487b3b73c1fa72c56f5c89430617296cd380373e7af3a538a82d4cd6d", size = 30711, upload-time = "2025-04-29T13:33:19.571Z" }, + { url = "https://files.pythonhosted.org/packages/e3/fb/5e9b5068df9e9f31a722a775a5e8322a29a638eaaa3eac5ea7f0b35e6314/setproctitle-1.3.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d6252098e98129a1decb59b46920d4eca17b0395f3d71b0d327d086fefe77d", size = 31742, upload-time = "2025-04-29T13:33:21.172Z" }, + { url = "https://files.pythonhosted.org/packages/35/88/54de1e73e8fce87d587889c7eedb48fc4ee2bbe4e4ca6331690d03024f86/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf355fbf0d4275d86f9f57be705d8e5eaa7f8ddb12b24ced2ea6cbd68fdb14dc", size = 31925, upload-time = "2025-04-29T13:33:22.427Z" }, + { url = "https://files.pythonhosted.org/packages/f3/01/65948d7badd66e63e3db247b923143da142790fa293830fdecf832712c2d/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e288f8a162d663916060beb5e8165a8551312b08efee9cf68302687471a6545d", size = 30981, upload-time = "2025-04-29T13:33:23.739Z" }, + { url = "https://files.pythonhosted.org/packages/22/20/c495e61786f1d38d5dc340b9d9077fee9be3dfc7e89f515afe12e1526dbc/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b2e54f4a2dc6edf0f5ea5b1d0a608d2af3dcb5aa8c8eeab9c8841b23e1b054fe", size = 33209, upload-time = "2025-04-29T13:33:24.915Z" }, + { url = "https://files.pythonhosted.org/packages/98/3f/a457b8550fbd34d5b482fe20b8376b529e76bf1fbf9a474a6d9a641ab4ad/setproctitle-1.3.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b6f4abde9a2946f57e8daaf1160b2351bcf64274ef539e6675c1d945dbd75e2a", size = 31587, upload-time = "2025-04-29T13:33:26.123Z" }, + { url = "https://files.pythonhosted.org/packages/44/fe/743517340e5a635e3f1c4310baea20c16c66202f96a6f4cead222ffd6d84/setproctitle-1.3.6-cp312-cp312-win32.whl", hash = "sha256:db608db98ccc21248370d30044a60843b3f0f3d34781ceeea67067c508cd5a28", size = 11487, upload-time = "2025-04-29T13:33:27.403Z" }, + { url = "https://files.pythonhosted.org/packages/60/9a/d88f1c1f0f4efff1bd29d9233583ee341114dda7d9613941453984849674/setproctitle-1.3.6-cp312-cp312-win_amd64.whl", hash = "sha256:082413db8a96b1f021088e8ec23f0a61fec352e649aba20881895815388b66d3", size = 12208, upload-time = "2025-04-29T13:33:28.852Z" }, ] [[package]] name = "setuptools" -version = "80.0.0" +version = "80.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/80/97e25f0f1e4067677806084b7382a6ff9979f3d15119375c475c288db9d7/setuptools-80.0.0.tar.gz", hash = "sha256:c40a5b3729d58dd749c0f08f1a07d134fb8a0a3d7f87dc33e7c5e1f762138650", size = 1354221, upload_time = "2025-04-27T17:21:10.806Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/b2/bd26ed086b842b68c8fe9aac380ad7e5118cf84fa7abd45bb059a88368a8/setuptools-80.1.0.tar.gz", hash = "sha256:2e308396e1d83de287ada2c2fd6e64286008fe6aca5008e0b6a8cb0e2c86eedd", size = 1354038, upload-time = "2025-04-30T17:41:06.171Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/63/5517029d6696ddf2bd378d46f63f479be001c31b462303170a1da57650cb/setuptools-80.0.0-py3-none-any.whl", hash = "sha256:a38f898dcd6e5380f4da4381a87ec90bd0a7eec23d204a5552e80ee3cab6bd27", size = 1240907, upload_time = "2025-04-27T17:21:09.175Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f6/126c9309c8fe93e5d6bb850593cd58d591daf2da45cc78b61e48d8d95879/setuptools-80.1.0-py3-none-any.whl", hash = "sha256:ea0e7655c05b74819f82e76e11a85b31779fee7c4969e82f72bab0664e8317e4", size = 1240689, upload-time = "2025-04-30T17:41:03.789Z" }, ] [[package]] @@ -4828,71 +4828,71 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload_time = "2025-04-03T09:15:05.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload-time = "2025-04-03T09:15:05.725Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/37/ae448f06f363ff3dfe4bae890abd842c4e3e9edaf01245dbc9b97008c9e6/shapely-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8323031ef7c1bdda7a92d5ddbc7b6b62702e73ba37e9a8ccc8da99ec2c0b87c", size = 1820974, upload_time = "2025-04-03T09:14:11.301Z" }, - { url = "https://files.pythonhosted.org/packages/78/da/ea2a898e93c6953c5eef353a0e1781a0013a1352f2b90aa9ab0b800e0c75/shapely-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4da7c6cd748d86ec6aace99ad17129d30954ccf5e73e9911cdb5f0fa9658b4f8", size = 1624137, upload_time = "2025-04-03T09:14:13.127Z" }, - { url = "https://files.pythonhosted.org/packages/64/4a/f903f82f0fabcd3f43ea2e8132cabda079119247330a9fe58018c39c4e22/shapely-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f0cdf85ff80831137067e7a237085a3ee72c225dba1b30beef87f7d396cf02b", size = 2957161, upload_time = "2025-04-03T09:14:15.031Z" }, - { url = "https://files.pythonhosted.org/packages/92/07/3e2738c542d73182066196b8ce99388cb537d19e300e428d50b1537e3b21/shapely-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f2be5d79aac39886f23000727cf02001aef3af8810176c29ee12cdc3ef3a50", size = 3078530, upload_time = "2025-04-03T09:14:16.562Z" }, - { url = "https://files.pythonhosted.org/packages/82/08/32210e63d8f8af9142d37c2433ece4846862cdac91a0fe66f040780a71bd/shapely-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:21a4515009f56d7a159cf5c2554264e82f56405b4721f9a422cb397237c5dca8", size = 3902208, upload_time = "2025-04-03T09:14:18.342Z" }, - { url = "https://files.pythonhosted.org/packages/19/0e/0abb5225f8a32fbdb615476637038a7d2db40c0af46d1bb3a08b869bee39/shapely-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cebc323cec2cb6b2eaa310fdfc621f6dbbfaf6bde336d13838fcea76c885a9", size = 4082863, upload_time = "2025-04-03T09:14:20.233Z" }, - { url = "https://files.pythonhosted.org/packages/f8/1b/7cd816fd388108c872ab7e2930180b02d0c34891213f361e4a66e5e032f2/shapely-2.1.0-cp311-cp311-win32.whl", hash = "sha256:cad51b7a5c8f82f5640472944a74f0f239123dde9a63042b3c5ea311739b7d20", size = 1527488, upload_time = "2025-04-03T09:14:21.597Z" }, - { url = "https://files.pythonhosted.org/packages/fd/28/7bb5b1944d4002d4b2f967762018500381c3b532f98e456bbda40c3ded68/shapely-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4005309dde8658e287ad9c435c81877f6a95a9419b932fa7a1f34b120f270ae", size = 1708311, upload_time = "2025-04-03T09:14:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d1/6a9371ec39d3ef08e13225594e6c55b045209629afd9e6d403204507c2a8/shapely-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53e7ee8bd8609cf12ee6dce01ea5affe676976cf7049315751d53d8db6d2b4b2", size = 1830732, upload_time = "2025-04-03T09:14:25.047Z" }, - { url = "https://files.pythonhosted.org/packages/32/87/799e3e48be7ce848c08509b94d2180f4ddb02e846e3c62d0af33da4d78d3/shapely-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cab20b665d26dbec0b380e15749bea720885a481fa7b1eedc88195d4a98cfa4", size = 1638404, upload_time = "2025-04-03T09:14:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/85/00/6665d77f9dd09478ab0993b8bc31668aec4fd3e5f1ddd1b28dd5830e47be/shapely-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a38b39a09340273c3c92b3b9a374272a12cc7e468aeeea22c1c46217a03e5c", size = 2945316, upload_time = "2025-04-03T09:14:28.266Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/738e07d10bbc67cae0dcfe5a484c6e518a517f4f90550dda2adf3a78b9f2/shapely-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edaec656bdd9b71278b98e6f77c464b1c3b2daa9eace78012ff0f0b4b5b15b04", size = 3063099, upload_time = "2025-04-03T09:14:30.067Z" }, - { url = "https://files.pythonhosted.org/packages/88/b8/138098674559362ab29f152bff3b6630de423378fbb0324812742433a4ef/shapely-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c8a732ddd9b25e7a54aa748e7df8fd704e23e5d5d35b7d376d80bffbfc376d04", size = 3887873, upload_time = "2025-04-03T09:14:31.912Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fdae7c2db009244991d86f4d2ca09d2f5ccc9d41c312c3b1ee1404dc55da/shapely-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9c93693ad8adfdc9138a5a2d42da02da94f728dd2e82d2f0f442f10e25027f5f", size = 4067004, upload_time = "2025-04-03T09:14:33.976Z" }, - { url = "https://files.pythonhosted.org/packages/ed/78/17e17d91b489019379df3ee1afc4bd39787b232aaa1d540f7d376f0280b7/shapely-2.1.0-cp312-cp312-win32.whl", hash = "sha256:d8ac6604eefe807e71a908524de23a37920133a1729fe3a4dfe0ed82c044cbf4", size = 1527366, upload_time = "2025-04-03T09:14:35.348Z" }, - { url = "https://files.pythonhosted.org/packages/b8/bd/9249bd6dda948441e25e4fb14cbbb5205146b0fff12c66b19331f1ff2141/shapely-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:f4f47e631aa4f9ec5576eac546eb3f38802e2f82aeb0552f9612cb9a14ece1db", size = 1708265, upload_time = "2025-04-03T09:14:36.878Z" }, + { url = "https://files.pythonhosted.org/packages/1c/37/ae448f06f363ff3dfe4bae890abd842c4e3e9edaf01245dbc9b97008c9e6/shapely-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8323031ef7c1bdda7a92d5ddbc7b6b62702e73ba37e9a8ccc8da99ec2c0b87c", size = 1820974, upload-time = "2025-04-03T09:14:11.301Z" }, + { url = "https://files.pythonhosted.org/packages/78/da/ea2a898e93c6953c5eef353a0e1781a0013a1352f2b90aa9ab0b800e0c75/shapely-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4da7c6cd748d86ec6aace99ad17129d30954ccf5e73e9911cdb5f0fa9658b4f8", size = 1624137, upload-time = "2025-04-03T09:14:13.127Z" }, + { url = "https://files.pythonhosted.org/packages/64/4a/f903f82f0fabcd3f43ea2e8132cabda079119247330a9fe58018c39c4e22/shapely-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f0cdf85ff80831137067e7a237085a3ee72c225dba1b30beef87f7d396cf02b", size = 2957161, upload-time = "2025-04-03T09:14:15.031Z" }, + { url = "https://files.pythonhosted.org/packages/92/07/3e2738c542d73182066196b8ce99388cb537d19e300e428d50b1537e3b21/shapely-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f2be5d79aac39886f23000727cf02001aef3af8810176c29ee12cdc3ef3a50", size = 3078530, upload-time = "2025-04-03T09:14:16.562Z" }, + { url = "https://files.pythonhosted.org/packages/82/08/32210e63d8f8af9142d37c2433ece4846862cdac91a0fe66f040780a71bd/shapely-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:21a4515009f56d7a159cf5c2554264e82f56405b4721f9a422cb397237c5dca8", size = 3902208, upload-time = "2025-04-03T09:14:18.342Z" }, + { url = "https://files.pythonhosted.org/packages/19/0e/0abb5225f8a32fbdb615476637038a7d2db40c0af46d1bb3a08b869bee39/shapely-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cebc323cec2cb6b2eaa310fdfc621f6dbbfaf6bde336d13838fcea76c885a9", size = 4082863, upload-time = "2025-04-03T09:14:20.233Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1b/7cd816fd388108c872ab7e2930180b02d0c34891213f361e4a66e5e032f2/shapely-2.1.0-cp311-cp311-win32.whl", hash = "sha256:cad51b7a5c8f82f5640472944a74f0f239123dde9a63042b3c5ea311739b7d20", size = 1527488, upload-time = "2025-04-03T09:14:21.597Z" }, + { url = "https://files.pythonhosted.org/packages/fd/28/7bb5b1944d4002d4b2f967762018500381c3b532f98e456bbda40c3ded68/shapely-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4005309dde8658e287ad9c435c81877f6a95a9419b932fa7a1f34b120f270ae", size = 1708311, upload-time = "2025-04-03T09:14:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d1/6a9371ec39d3ef08e13225594e6c55b045209629afd9e6d403204507c2a8/shapely-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53e7ee8bd8609cf12ee6dce01ea5affe676976cf7049315751d53d8db6d2b4b2", size = 1830732, upload-time = "2025-04-03T09:14:25.047Z" }, + { url = "https://files.pythonhosted.org/packages/32/87/799e3e48be7ce848c08509b94d2180f4ddb02e846e3c62d0af33da4d78d3/shapely-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cab20b665d26dbec0b380e15749bea720885a481fa7b1eedc88195d4a98cfa4", size = 1638404, upload-time = "2025-04-03T09:14:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/85/00/6665d77f9dd09478ab0993b8bc31668aec4fd3e5f1ddd1b28dd5830e47be/shapely-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a38b39a09340273c3c92b3b9a374272a12cc7e468aeeea22c1c46217a03e5c", size = 2945316, upload-time = "2025-04-03T09:14:28.266Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/738e07d10bbc67cae0dcfe5a484c6e518a517f4f90550dda2adf3a78b9f2/shapely-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edaec656bdd9b71278b98e6f77c464b1c3b2daa9eace78012ff0f0b4b5b15b04", size = 3063099, upload-time = "2025-04-03T09:14:30.067Z" }, + { url = "https://files.pythonhosted.org/packages/88/b8/138098674559362ab29f152bff3b6630de423378fbb0324812742433a4ef/shapely-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c8a732ddd9b25e7a54aa748e7df8fd704e23e5d5d35b7d376d80bffbfc376d04", size = 3887873, upload-time = "2025-04-03T09:14:31.912Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fdae7c2db009244991d86f4d2ca09d2f5ccc9d41c312c3b1ee1404dc55da/shapely-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9c93693ad8adfdc9138a5a2d42da02da94f728dd2e82d2f0f442f10e25027f5f", size = 4067004, upload-time = "2025-04-03T09:14:33.976Z" }, + { url = "https://files.pythonhosted.org/packages/ed/78/17e17d91b489019379df3ee1afc4bd39787b232aaa1d540f7d376f0280b7/shapely-2.1.0-cp312-cp312-win32.whl", hash = "sha256:d8ac6604eefe807e71a908524de23a37920133a1729fe3a4dfe0ed82c044cbf4", size = 1527366, upload-time = "2025-04-03T09:14:35.348Z" }, + { url = "https://files.pythonhosted.org/packages/b8/bd/9249bd6dda948441e25e4fb14cbbb5205146b0fff12c66b19331f1ff2141/shapely-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:f4f47e631aa4f9ec5576eac546eb3f38802e2f82aeb0552f9612cb9a14ece1db", size = 1708265, upload-time = "2025-04-03T09:14:36.878Z" }, ] [[package]] name = "siphash24" version = "1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801, upload_time = "2024-10-15T13:41:51.924Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/be/f0a0ffbb00c51c5633b41459b5ce9b017c025a9256b4403e648c18e70850/siphash24-1.7.tar.gz", hash = "sha256:6e90fee5f199ea25b4e7303646b31872a437174fe885a93dbd4cf7784eb48164", size = 19801, upload-time = "2024-10-15T13:41:51.924Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493, upload_time = "2024-10-15T13:41:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350, upload_time = "2024-10-15T13:41:16.262Z" }, - { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567, upload_time = "2024-10-15T13:41:17.435Z" }, - { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630, upload_time = "2024-10-15T13:41:18.578Z" }, - { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648, upload_time = "2024-10-15T13:41:19.606Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046, upload_time = "2024-10-15T13:41:20.654Z" }, - { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084, upload_time = "2024-10-15T13:41:21.776Z" }, - { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233, upload_time = "2024-10-15T13:41:22.787Z" }, - { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188, upload_time = "2024-10-15T13:41:24.327Z" }, - { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946, upload_time = "2024-10-15T13:41:25.633Z" }, - { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323, upload_time = "2024-10-15T13:41:27.349Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000, upload_time = "2024-10-15T13:41:28.364Z" }, + { url = "https://files.pythonhosted.org/packages/4e/67/4ffd23a848739966e1b314ef99f6410035bccee00be14261313787b8f506/siphash24-1.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de75488e93f1cd12c8d5004efd1ebd958c0265205a9d73e8dd8b071900838841", size = 80493, upload-time = "2024-10-15T13:41:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/56/bd/ec198a8c7aef65e967ae84f633bd9950d784c9e527d738c9a3e4bccc34a5/siphash24-1.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffca9908450f9f346e97a223185fcd16217d67d84c6f246f3080c4224f41a514", size = 75350, upload-time = "2024-10-15T13:41:16.262Z" }, + { url = "https://files.pythonhosted.org/packages/50/5a/77838c916bd15addfc2e51286db4c442cb12e25eb4f8d296c394c2280240/siphash24-1.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8ff44ce166452993fea267ea1b2fd089d8e7f103b13d360da441f12b0df121d", size = 100567, upload-time = "2024-10-15T13:41:17.435Z" }, + { url = "https://files.pythonhosted.org/packages/f0/aa/736a0a2efae9a6f69ac1ee4d28c2274fcad2150349fac752d6c525c4e06e/siphash24-1.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4062548dcb1eef13bbe0356d6f8675bfe4571ef38d7103445daa82ba167240d1", size = 105630, upload-time = "2024-10-15T13:41:18.578Z" }, + { url = "https://files.pythonhosted.org/packages/79/52/1afbd70142d3db093d49197e3abe15ca2f1a14678299327ba776944b4771/siphash24-1.7-cp311-cp311-win32.whl", hash = "sha256:7b4ea29376b688fbcc3d25707c15a9dfe7b4ebbc4322878d75bb77e199210a39", size = 67648, upload-time = "2024-10-15T13:41:19.606Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1d/bedcd04c2d1d199c9f6b3e61a6caae0e17257696c9f49594e49856b17a99/siphash24-1.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec06104e6ef1e512ee30f1b8aeae2b83c0f55f12a94042f0df5a87d43a1f4c52", size = 80046, upload-time = "2024-10-15T13:41:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/3e/62/93e552af9535a416f684327f870143ee42fc9e816091672467cdfd62cce6/siphash24-1.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:76a64ff0cdd192e4d0a391956d9b121c56ed56e773c5ab7eb7c3e035fd16e8cb", size = 82084, upload-time = "2024-10-15T13:41:21.776Z" }, + { url = "https://files.pythonhosted.org/packages/59/3e/b0791ab53aa9ac191b71a021eab2e75baa7c27d7feb7ec148d7961d148ba/siphash24-1.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ca649bc7437d614f758891deade3b187832792a853269219e77f10509f82fe", size = 76233, upload-time = "2024-10-15T13:41:22.787Z" }, + { url = "https://files.pythonhosted.org/packages/29/4c/4c1b809bf302e9b60f3ec09ba115b2a4ac1ff6755735ee8884924fcdb45e/siphash24-1.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc37dd0aed23f76bd257fbd2953fd5d954b329d7463c6ff57263a2699c52dde6", size = 98188, upload-time = "2024-10-15T13:41:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/96/bf/e6b49f8ff88130bd224f291ea77d30fdde4df5f6572c519aca5d8fc8a27c/siphash24-1.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eea490a200891905856b6ad0f9c56d4ec787876220bcb34c49441b2566b97887", size = 102946, upload-time = "2024-10-15T13:41:25.633Z" }, + { url = "https://files.pythonhosted.org/packages/3d/75/45c831626013950fb2ea715c218c3397e5cf2328a67208bf5d8ff69aa9e6/siphash24-1.7-cp312-cp312-win32.whl", hash = "sha256:69eb8c2c112a738875bb283cd53ef5e86874bc5aed17f3020b38e9174208fb79", size = 68323, upload-time = "2024-10-15T13:41:27.349Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d3/39190c40a68defd19b99c1082dd7455543a52283803bfa111b0e45fae968/siphash24-1.7-cp312-cp312-win_amd64.whl", hash = "sha256:7459569ea4669b6feeaf7d299fc5157cc5c69ca1231dc0decb7a7da2397c782e", size = 81000, upload-time = "2024-10-15T13:41:28.364Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload_time = "2024-12-04T17:35:28.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload_time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "smbus2" version = "0.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/c9/6d85aa809e107adf85303010a59b340be109c8f815cbedc5c08c73bcffef/smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035", size = 16950, upload_time = "2024-10-19T09:20:56.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/10/c9/6d85aa809e107adf85303010a59b340be109c8f815cbedc5c08c73bcffef/smbus2-0.5.0.tar.gz", hash = "sha256:4a5946fd82277870c2878befdb1a29bb28d15cda14ea4d8d2d54cf3d4bdcb035", size = 16950, upload-time = "2024-10-19T09:20:56.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/9f/2235ba9001e3c29fc342eeb222104420bcb7bac51555f0c034376a744075/smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949", size = 11527, upload_time = "2024-10-19T09:20:55.202Z" }, + { url = "https://files.pythonhosted.org/packages/85/9f/2235ba9001e3c29fc342eeb222104420bcb7bac51555f0c034376a744075/smbus2-0.5.0-py2.py3-none-any.whl", hash = "sha256:1a15c3b9fa69357beb038cc0b5d37939702f8bfde1ddc89ca9f17d8461dbe949", size = 11527, upload-time = "2024-10-19T09:20:55.202Z" }, ] [[package]] name = "sortedcontainers" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload_time = "2021-05-16T22:03:42.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload_time = "2021-05-16T22:03:41.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, ] [[package]] @@ -4902,19 +4902,19 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload_time = "2024-10-12T09:40:12.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload-time = "2024-10-12T09:40:12.24Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload_time = "2024-10-12T09:40:05.605Z" }, - { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload_time = "2024-10-12T09:40:07.436Z" }, - { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload_time = "2024-10-12T09:40:09.355Z" }, - { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload_time = "2024-10-12T09:40:11.065Z" }, + { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload-time = "2024-10-12T09:40:05.605Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload-time = "2024-10-12T09:40:07.436Z" }, + { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload-time = "2024-10-12T09:40:09.355Z" }, + { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload-time = "2024-10-12T09:40:11.065Z" }, ] [[package]] name = "spidev" version = "3.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/401c0a7be089e02826cf2c201f489876b601f15be100fe391ef9c2faed83/spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b", size = 11917, upload_time = "2022-11-03T20:39:08.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/401c0a7be089e02826cf2c201f489876b601f15be100fe391ef9c2faed83/spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b", size = 11917, upload-time = "2022-11-03T20:39:08.257Z" } [[package]] name = "sympy" @@ -4923,47 +4923,47 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mpmath" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload_time = "2025-04-27T18:05:01.611Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload_time = "2025-04-27T18:04:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] [[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, upload_time = "2022-10-06T17:21:48.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload_time = "2022-10-06T17:21:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload_time = "2024-11-27T22:38:36.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload_time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload_time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload_time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload_time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload_time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload_time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload_time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload_time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload_time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload_time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload_time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload_time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload_time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload_time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload_time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload_time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload_time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload_time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload_time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload_time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload_time = "2024-11-27T22:38:35.385Z" }, + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] @@ -4973,9 +4973,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload_time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload_time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] [[package]] @@ -4985,69 +4985,69 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload_time = "2025-03-28T02:55:13.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload-time = "2025-03-28T02:55:13.271Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload_time = "2025-03-28T02:55:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload-time = "2025-03-28T02:55:11.946Z" }, ] [[package]] name = "types-tabulate" version = "0.9.0.20241207" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/43/16030404a327e4ff8c692f2273854019ed36718667b2993609dc37d14dd4/types_tabulate-0.9.0.20241207.tar.gz", hash = "sha256:ac1ac174750c0a385dfd248edc6279fa328aaf4ea317915ab879a2ec47833230", size = 8195, upload_time = "2024-12-07T02:54:42.554Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/43/16030404a327e4ff8c692f2273854019ed36718667b2993609dc37d14dd4/types_tabulate-0.9.0.20241207.tar.gz", hash = "sha256:ac1ac174750c0a385dfd248edc6279fa328aaf4ea317915ab879a2ec47833230", size = 8195, upload-time = "2024-12-07T02:54:42.554Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/86/a9ebfd509cbe74471106dffed320e208c72537f9aeb0a55eaa6b1b5e4d17/types_tabulate-0.9.0.20241207-py3-none-any.whl", hash = "sha256:b8dad1343c2a8ba5861c5441370c3e35908edd234ff036d4298708a1d4cf8a85", size = 8307, upload_time = "2024-12-07T02:54:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/5e/86/a9ebfd509cbe74471106dffed320e208c72537f9aeb0a55eaa6b1b5e4d17/types_tabulate-0.9.0.20241207-py3-none-any.whl", hash = "sha256:b8dad1343c2a8ba5861c5441370c3e35908edd234ff036d4298708a1d4cf8a85", size = 8307, upload-time = "2024-12-07T02:54:41.031Z" }, ] [[package]] name = "typing-extensions" version = "4.13.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload_time = "2025-04-10T14:19:05.416Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload_time = "2025-04-10T14:19:03.967Z" }, + { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, ] [[package]] name = "urllib3" version = "2.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload_time = "2025-04-10T15:23:39.232Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload_time = "2025-04-10T15:23:37.377Z" }, + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, ] [[package]] name = "watchdog" 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, upload_time = "2024-11-01T14:07:13.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } wheels = [ - { 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, upload_time = "2024-11-01T14:06:31.756Z" }, - { 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, upload_time = "2024-11-01T14:06:32.99Z" }, - { 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, upload_time = "2024-11-01T14:06:34.963Z" }, - { 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, upload_time = "2024-11-01T14:06:37.745Z" }, - { 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, upload_time = "2024-11-01T14:06:39.748Z" }, - { 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, upload_time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload_time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload_time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload_time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload_time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload_time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload_time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload_time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload_time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload_time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload_time = "2024-11-01T14:07:11.845Z" }, + { 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, upload-time = "2024-11-01T14:06:31.756Z" }, + { 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, upload-time = "2024-11-01T14:06:32.99Z" }, + { 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, upload-time = "2024-11-01T14:06:34.963Z" }, + { 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, upload-time = "2024-11-01T14:06:37.745Z" }, + { 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, upload-time = "2024-11-01T14:06:39.748Z" }, + { 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, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] [[package]] name = "websocket-client" version = "1.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload_time = "2024-04-23T22:16:16.976Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload_time = "2024-04-23T22:16:14.422Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, ] [[package]] @@ -5057,9 +5057,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload_time = "2024-11-14T00:11:41.584Z" } +sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158, upload_time = "2024-11-14T00:11:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158, upload-time = "2024-11-14T00:11:39.37Z" }, ] [[package]] @@ -5071,43 +5071,43 @@ dependencies = [ { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/62/51/c0edba5219027f6eab262e139f73e2417b0f4efffa23bf562f6e18f76ca5/yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307", size = 185258, upload_time = "2025-04-17T00:45:14.661Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/51/c0edba5219027f6eab262e139f73e2417b0f4efffa23bf562f6e18f76ca5/yarl-1.20.0.tar.gz", hash = "sha256:686d51e51ee5dfe62dec86e4866ee0e9ed66df700d55c828a615640adc885307", size = 185258, upload-time = "2025-04-17T00:45:14.661Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/82/a59d8e21b20ffc836775fa7daedac51d16bb8f3010c4fcb495c4496aa922/yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3", size = 145178, upload_time = "2025-04-17T00:42:04.511Z" }, - { url = "https://files.pythonhosted.org/packages/ba/81/315a3f6f95947cfbf37c92d6fbce42a1a6207b6c38e8c2b452499ec7d449/yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a", size = 96859, upload_time = "2025-04-17T00:42:06.43Z" }, - { url = "https://files.pythonhosted.org/packages/ad/17/9b64e575583158551b72272a1023cdbd65af54fe13421d856b2850a6ddb7/yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2", size = 94647, upload_time = "2025-04-17T00:42:07.976Z" }, - { url = "https://files.pythonhosted.org/packages/2c/29/8f291e7922a58a21349683f6120a85701aeefaa02e9f7c8a2dc24fe3f431/yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e", size = 355788, upload_time = "2025-04-17T00:42:09.902Z" }, - { url = "https://files.pythonhosted.org/packages/26/6d/b4892c80b805c42c228c6d11e03cafabf81662d371b0853e7f0f513837d5/yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9", size = 344613, upload_time = "2025-04-17T00:42:11.768Z" }, - { url = "https://files.pythonhosted.org/packages/d7/0e/517aa28d3f848589bae9593717b063a544b86ba0a807d943c70f48fcf3bb/yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a", size = 370953, upload_time = "2025-04-17T00:42:13.983Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9b/5bd09d2f1ad6e6f7c2beae9e50db78edd2cca4d194d227b958955573e240/yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2", size = 369204, upload_time = "2025-04-17T00:42:16.386Z" }, - { url = "https://files.pythonhosted.org/packages/9c/85/d793a703cf4bd0d4cd04e4b13cc3d44149470f790230430331a0c1f52df5/yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2", size = 358108, upload_time = "2025-04-17T00:42:18.622Z" }, - { url = "https://files.pythonhosted.org/packages/6f/54/b6c71e13549c1f6048fbc14ce8d930ac5fb8bafe4f1a252e621a24f3f1f9/yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8", size = 346610, upload_time = "2025-04-17T00:42:20.9Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1a/d6087d58bdd0d8a2a37bbcdffac9d9721af6ebe50d85304d9f9b57dfd862/yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902", size = 365378, upload_time = "2025-04-17T00:42:22.926Z" }, - { url = "https://files.pythonhosted.org/packages/02/84/e25ddff4cbc001dbc4af76f8d41a3e23818212dd1f0a52044cbc60568872/yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791", size = 356919, upload_time = "2025-04-17T00:42:25.145Z" }, - { url = "https://files.pythonhosted.org/packages/04/76/898ae362353bf8f64636495d222c8014c8e5267df39b1a9fe1e1572fb7d0/yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f", size = 364248, upload_time = "2025-04-17T00:42:27.475Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b0/9d9198d83a622f1c40fdbf7bd13b224a6979f2e1fc2cf50bfb1d8773c495/yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da", size = 378418, upload_time = "2025-04-17T00:42:29.333Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ce/1f50c1cc594cf5d3f5bf4a9b616fca68680deaec8ad349d928445ac52eb8/yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4", size = 383850, upload_time = "2025-04-17T00:42:31.668Z" }, - { url = "https://files.pythonhosted.org/packages/89/1e/a59253a87b35bfec1a25bb5801fb69943330b67cfd266278eb07e0609012/yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5", size = 381218, upload_time = "2025-04-17T00:42:33.523Z" }, - { url = "https://files.pythonhosted.org/packages/85/b0/26f87df2b3044b0ef1a7cf66d321102bdca091db64c5ae853fcb2171c031/yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6", size = 86606, upload_time = "2025-04-17T00:42:35.873Z" }, - { url = "https://files.pythonhosted.org/packages/33/46/ca335c2e1f90446a77640a45eeb1cd8f6934f2c6e4df7db0f0f36ef9f025/yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb", size = 93374, upload_time = "2025-04-17T00:42:37.586Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e8/3efdcb83073df978bb5b1a9cc0360ce596680e6c3fac01f2a994ccbb8939/yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f", size = 147089, upload_time = "2025-04-17T00:42:39.602Z" }, - { url = "https://files.pythonhosted.org/packages/60/c3/9e776e98ea350f76f94dd80b408eaa54e5092643dbf65fd9babcffb60509/yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e", size = 97706, upload_time = "2025-04-17T00:42:41.469Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/45cdfb64a3b855ce074ae607b9fc40bc82e7613b94e7612b030255c93a09/yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e", size = 95719, upload_time = "2025-04-17T00:42:43.666Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4e/929633b249611eeed04e2f861a14ed001acca3ef9ec2a984a757b1515889/yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33", size = 343972, upload_time = "2025-04-17T00:42:45.391Z" }, - { url = "https://files.pythonhosted.org/packages/49/fd/047535d326c913f1a90407a3baf7ff535b10098611eaef2c527e32e81ca1/yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58", size = 339639, upload_time = "2025-04-17T00:42:47.552Z" }, - { url = "https://files.pythonhosted.org/packages/48/2f/11566f1176a78f4bafb0937c0072410b1b0d3640b297944a6a7a556e1d0b/yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f", size = 353745, upload_time = "2025-04-17T00:42:49.406Z" }, - { url = "https://files.pythonhosted.org/packages/26/17/07dfcf034d6ae8837b33988be66045dd52f878dfb1c4e8f80a7343f677be/yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae", size = 354178, upload_time = "2025-04-17T00:42:51.588Z" }, - { url = "https://files.pythonhosted.org/packages/15/45/212604d3142d84b4065d5f8cab6582ed3d78e4cc250568ef2a36fe1cf0a5/yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018", size = 349219, upload_time = "2025-04-17T00:42:53.674Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e0/a10b30f294111c5f1c682461e9459935c17d467a760c21e1f7db400ff499/yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672", size = 337266, upload_time = "2025-04-17T00:42:55.49Z" }, - { url = "https://files.pythonhosted.org/packages/33/a6/6efa1d85a675d25a46a167f9f3e80104cde317dfdf7f53f112ae6b16a60a/yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8", size = 360873, upload_time = "2025-04-17T00:42:57.895Z" }, - { url = "https://files.pythonhosted.org/packages/77/67/c8ab718cb98dfa2ae9ba0f97bf3cbb7d45d37f13fe1fbad25ac92940954e/yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7", size = 360524, upload_time = "2025-04-17T00:43:00.094Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e8/c3f18660cea1bc73d9f8a2b3ef423def8dadbbae6c4afabdb920b73e0ead/yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594", size = 365370, upload_time = "2025-04-17T00:43:02.242Z" }, - { url = "https://files.pythonhosted.org/packages/c9/99/33f3b97b065e62ff2d52817155a89cfa030a1a9b43fee7843ef560ad9603/yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6", size = 373297, upload_time = "2025-04-17T00:43:04.189Z" }, - { url = "https://files.pythonhosted.org/packages/3d/89/7519e79e264a5f08653d2446b26d4724b01198a93a74d2e259291d538ab1/yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1", size = 378771, upload_time = "2025-04-17T00:43:06.609Z" }, - { url = "https://files.pythonhosted.org/packages/3a/58/6c460bbb884abd2917c3eef6f663a4a873f8dc6f498561fc0ad92231c113/yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b", size = 375000, upload_time = "2025-04-17T00:43:09.01Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/dd7ed1aa23fea996834278d7ff178f215b24324ee527df53d45e34d21d28/yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64", size = 86355, upload_time = "2025-04-17T00:43:11.311Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c6/333fe0338305c0ac1c16d5aa7cc4841208d3252bbe62172e0051006b5445/yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c", size = 92904, upload_time = "2025-04-17T00:43:13.087Z" }, - { url = "https://files.pythonhosted.org/packages/ea/1f/70c57b3d7278e94ed22d85e09685d3f0a38ebdd8c5c73b65ba4c0d0fe002/yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124", size = 46124, upload_time = "2025-04-17T00:45:12.199Z" }, + { url = "https://files.pythonhosted.org/packages/60/82/a59d8e21b20ffc836775fa7daedac51d16bb8f3010c4fcb495c4496aa922/yarl-1.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fdb5204d17cb32b2de2d1e21c7461cabfacf17f3645e4b9039f210c5d3378bf3", size = 145178, upload-time = "2025-04-17T00:42:04.511Z" }, + { url = "https://files.pythonhosted.org/packages/ba/81/315a3f6f95947cfbf37c92d6fbce42a1a6207b6c38e8c2b452499ec7d449/yarl-1.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eaddd7804d8e77d67c28d154ae5fab203163bd0998769569861258e525039d2a", size = 96859, upload-time = "2025-04-17T00:42:06.43Z" }, + { url = "https://files.pythonhosted.org/packages/ad/17/9b64e575583158551b72272a1023cdbd65af54fe13421d856b2850a6ddb7/yarl-1.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:634b7ba6b4a85cf67e9df7c13a7fb2e44fa37b5d34501038d174a63eaac25ee2", size = 94647, upload-time = "2025-04-17T00:42:07.976Z" }, + { url = "https://files.pythonhosted.org/packages/2c/29/8f291e7922a58a21349683f6120a85701aeefaa02e9f7c8a2dc24fe3f431/yarl-1.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d409e321e4addf7d97ee84162538c7258e53792eb7c6defd0c33647d754172e", size = 355788, upload-time = "2025-04-17T00:42:09.902Z" }, + { url = "https://files.pythonhosted.org/packages/26/6d/b4892c80b805c42c228c6d11e03cafabf81662d371b0853e7f0f513837d5/yarl-1.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ea52f7328a36960ba3231c6677380fa67811b414798a6e071c7085c57b6d20a9", size = 344613, upload-time = "2025-04-17T00:42:11.768Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0e/517aa28d3f848589bae9593717b063a544b86ba0a807d943c70f48fcf3bb/yarl-1.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8703517b924463994c344dcdf99a2d5ce9eca2b6882bb640aa555fb5efc706a", size = 370953, upload-time = "2025-04-17T00:42:13.983Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/5bd09d2f1ad6e6f7c2beae9e50db78edd2cca4d194d227b958955573e240/yarl-1.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:077989b09ffd2f48fb2d8f6a86c5fef02f63ffe6b1dd4824c76de7bb01e4f2e2", size = 369204, upload-time = "2025-04-17T00:42:16.386Z" }, + { url = "https://files.pythonhosted.org/packages/9c/85/d793a703cf4bd0d4cd04e4b13cc3d44149470f790230430331a0c1f52df5/yarl-1.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0acfaf1da020253f3533526e8b7dd212838fdc4109959a2c53cafc6db611bff2", size = 358108, upload-time = "2025-04-17T00:42:18.622Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/b6c71e13549c1f6048fbc14ce8d930ac5fb8bafe4f1a252e621a24f3f1f9/yarl-1.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4230ac0b97ec5eeb91d96b324d66060a43fd0d2a9b603e3327ed65f084e41f8", size = 346610, upload-time = "2025-04-17T00:42:20.9Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1a/d6087d58bdd0d8a2a37bbcdffac9d9721af6ebe50d85304d9f9b57dfd862/yarl-1.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a6a1e6ae21cdd84011c24c78d7a126425148b24d437b5702328e4ba640a8902", size = 365378, upload-time = "2025-04-17T00:42:22.926Z" }, + { url = "https://files.pythonhosted.org/packages/02/84/e25ddff4cbc001dbc4af76f8d41a3e23818212dd1f0a52044cbc60568872/yarl-1.20.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:86de313371ec04dd2531f30bc41a5a1a96f25a02823558ee0f2af0beaa7ca791", size = 356919, upload-time = "2025-04-17T00:42:25.145Z" }, + { url = "https://files.pythonhosted.org/packages/04/76/898ae362353bf8f64636495d222c8014c8e5267df39b1a9fe1e1572fb7d0/yarl-1.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dd59c9dd58ae16eaa0f48c3d0cbe6be8ab4dc7247c3ff7db678edecbaf59327f", size = 364248, upload-time = "2025-04-17T00:42:27.475Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b0/9d9198d83a622f1c40fdbf7bd13b224a6979f2e1fc2cf50bfb1d8773c495/yarl-1.20.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a0bc5e05f457b7c1994cc29e83b58f540b76234ba6b9648a4971ddc7f6aa52da", size = 378418, upload-time = "2025-04-17T00:42:29.333Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ce/1f50c1cc594cf5d3f5bf4a9b616fca68680deaec8ad349d928445ac52eb8/yarl-1.20.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c9471ca18e6aeb0e03276b5e9b27b14a54c052d370a9c0c04a68cefbd1455eb4", size = 383850, upload-time = "2025-04-17T00:42:31.668Z" }, + { url = "https://files.pythonhosted.org/packages/89/1e/a59253a87b35bfec1a25bb5801fb69943330b67cfd266278eb07e0609012/yarl-1.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40ed574b4df723583a26c04b298b283ff171bcc387bc34c2683235e2487a65a5", size = 381218, upload-time = "2025-04-17T00:42:33.523Z" }, + { url = "https://files.pythonhosted.org/packages/85/b0/26f87df2b3044b0ef1a7cf66d321102bdca091db64c5ae853fcb2171c031/yarl-1.20.0-cp311-cp311-win32.whl", hash = "sha256:db243357c6c2bf3cd7e17080034ade668d54ce304d820c2a58514a4e51d0cfd6", size = 86606, upload-time = "2025-04-17T00:42:35.873Z" }, + { url = "https://files.pythonhosted.org/packages/33/46/ca335c2e1f90446a77640a45eeb1cd8f6934f2c6e4df7db0f0f36ef9f025/yarl-1.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c12cd754d9dbd14204c328915e23b0c361b88f3cffd124129955e60a4fbfcfb", size = 93374, upload-time = "2025-04-17T00:42:37.586Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e8/3efdcb83073df978bb5b1a9cc0360ce596680e6c3fac01f2a994ccbb8939/yarl-1.20.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e06b9f6cdd772f9b665e5ba8161968e11e403774114420737f7884b5bd7bdf6f", size = 147089, upload-time = "2025-04-17T00:42:39.602Z" }, + { url = "https://files.pythonhosted.org/packages/60/c3/9e776e98ea350f76f94dd80b408eaa54e5092643dbf65fd9babcffb60509/yarl-1.20.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b9ae2fbe54d859b3ade40290f60fe40e7f969d83d482e84d2c31b9bff03e359e", size = 97706, upload-time = "2025-04-17T00:42:41.469Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/45cdfb64a3b855ce074ae607b9fc40bc82e7613b94e7612b030255c93a09/yarl-1.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d12b8945250d80c67688602c891237994d203d42427cb14e36d1a732eda480e", size = 95719, upload-time = "2025-04-17T00:42:43.666Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4e/929633b249611eeed04e2f861a14ed001acca3ef9ec2a984a757b1515889/yarl-1.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087e9731884621b162a3e06dc0d2d626e1542a617f65ba7cc7aeab279d55ad33", size = 343972, upload-time = "2025-04-17T00:42:45.391Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/047535d326c913f1a90407a3baf7ff535b10098611eaef2c527e32e81ca1/yarl-1.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69df35468b66c1a6e6556248e6443ef0ec5f11a7a4428cf1f6281f1879220f58", size = 339639, upload-time = "2025-04-17T00:42:47.552Z" }, + { url = "https://files.pythonhosted.org/packages/48/2f/11566f1176a78f4bafb0937c0072410b1b0d3640b297944a6a7a556e1d0b/yarl-1.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2992fe29002fd0d4cbaea9428b09af9b8686a9024c840b8a2b8f4ea4abc16f", size = 353745, upload-time = "2025-04-17T00:42:49.406Z" }, + { url = "https://files.pythonhosted.org/packages/26/17/07dfcf034d6ae8837b33988be66045dd52f878dfb1c4e8f80a7343f677be/yarl-1.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c903e0b42aab48abfbac668b5a9d7b6938e721a6341751331bcd7553de2dcae", size = 354178, upload-time = "2025-04-17T00:42:51.588Z" }, + { url = "https://files.pythonhosted.org/packages/15/45/212604d3142d84b4065d5f8cab6582ed3d78e4cc250568ef2a36fe1cf0a5/yarl-1.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf099e2432131093cc611623e0b0bcc399b8cddd9a91eded8bfb50402ec35018", size = 349219, upload-time = "2025-04-17T00:42:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e0/a10b30f294111c5f1c682461e9459935c17d467a760c21e1f7db400ff499/yarl-1.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a7f62f5dc70a6c763bec9ebf922be52aa22863d9496a9a30124d65b489ea672", size = 337266, upload-time = "2025-04-17T00:42:55.49Z" }, + { url = "https://files.pythonhosted.org/packages/33/a6/6efa1d85a675d25a46a167f9f3e80104cde317dfdf7f53f112ae6b16a60a/yarl-1.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:54ac15a8b60382b2bcefd9a289ee26dc0920cf59b05368c9b2b72450751c6eb8", size = 360873, upload-time = "2025-04-17T00:42:57.895Z" }, + { url = "https://files.pythonhosted.org/packages/77/67/c8ab718cb98dfa2ae9ba0f97bf3cbb7d45d37f13fe1fbad25ac92940954e/yarl-1.20.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:25b3bc0763a7aca16a0f1b5e8ef0f23829df11fb539a1b70476dcab28bd83da7", size = 360524, upload-time = "2025-04-17T00:43:00.094Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/c3f18660cea1bc73d9f8a2b3ef423def8dadbbae6c4afabdb920b73e0ead/yarl-1.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2586e36dc070fc8fad6270f93242124df68b379c3a251af534030a4a33ef594", size = 365370, upload-time = "2025-04-17T00:43:02.242Z" }, + { url = "https://files.pythonhosted.org/packages/c9/99/33f3b97b065e62ff2d52817155a89cfa030a1a9b43fee7843ef560ad9603/yarl-1.20.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:866349da9d8c5290cfefb7fcc47721e94de3f315433613e01b435473be63daa6", size = 373297, upload-time = "2025-04-17T00:43:04.189Z" }, + { url = "https://files.pythonhosted.org/packages/3d/89/7519e79e264a5f08653d2446b26d4724b01198a93a74d2e259291d538ab1/yarl-1.20.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:33bb660b390a0554d41f8ebec5cd4475502d84104b27e9b42f5321c5192bfcd1", size = 378771, upload-time = "2025-04-17T00:43:06.609Z" }, + { url = "https://files.pythonhosted.org/packages/3a/58/6c460bbb884abd2917c3eef6f663a4a873f8dc6f498561fc0ad92231c113/yarl-1.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:737e9f171e5a07031cbee5e9180f6ce21a6c599b9d4b2c24d35df20a52fabf4b", size = 375000, upload-time = "2025-04-17T00:43:09.01Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/dd7ed1aa23fea996834278d7ff178f215b24324ee527df53d45e34d21d28/yarl-1.20.0-cp312-cp312-win32.whl", hash = "sha256:839de4c574169b6598d47ad61534e6981979ca2c820ccb77bf70f4311dd2cc64", size = 86355, upload-time = "2025-04-17T00:43:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c6/333fe0338305c0ac1c16d5aa7cc4841208d3252bbe62172e0051006b5445/yarl-1.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:3d7dbbe44b443b0c4aa0971cb07dcb2c2060e4a9bf8d1301140a33a93c98e18c", size = 92904, upload-time = "2025-04-17T00:43:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/ea/1f/70c57b3d7278e94ed22d85e09685d3f0a38ebdd8c5c73b65ba4c0d0fe002/yarl-1.20.0-py3-none-any.whl", hash = "sha256:5d0fe6af927a47a230f31e6004621fd0959eaa915fc62acfafa67ff7229a3124", size = 46124, upload-time = "2025-04-17T00:45:12.199Z" }, ] [[package]] @@ -5117,38 +5117,38 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload_time = "2024-07-15T00:18:06.141Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701, upload-time = "2024-07-15T00:18:06.141Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload_time = "2024-07-15T00:14:04.909Z" }, - { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload_time = "2024-07-15T00:14:13.99Z" }, - { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload_time = "2024-07-15T00:14:16.588Z" }, - { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload_time = "2024-07-15T00:14:19.389Z" }, - { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload_time = "2024-07-15T00:14:22.173Z" }, - { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload_time = "2024-07-15T00:14:24.825Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload_time = "2024-07-15T00:14:26.982Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload_time = "2024-07-15T00:14:29.582Z" }, - { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload_time = "2024-07-15T00:14:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload_time = "2024-07-15T00:14:42.786Z" }, - { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload_time = "2024-07-15T00:14:45.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload_time = "2024-07-15T00:14:47.407Z" }, - { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload_time = "2024-07-15T00:15:03.529Z" }, - { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload_time = "2024-07-15T00:15:28.372Z" }, - { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload_time = "2024-07-15T00:15:32.26Z" }, - { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload_time = "2024-07-15T00:15:34.004Z" }, - { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload_time = "2024-07-15T00:15:35.815Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload_time = "2024-07-15T00:15:37.995Z" }, - { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload_time = "2024-07-15T00:15:39.872Z" }, - { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload_time = "2024-07-15T00:15:41.75Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload_time = "2024-07-15T00:15:44.114Z" }, - { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload_time = "2024-07-15T00:15:46.509Z" }, - { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload_time = "2024-07-15T00:15:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload_time = "2024-07-15T00:15:52.025Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload_time = "2024-07-15T00:15:54.971Z" }, - { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload_time = "2024-07-15T00:15:57.634Z" }, - { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload_time = "2024-07-15T00:16:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload_time = "2024-07-15T00:16:03.669Z" }, - { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload_time = "2024-07-15T00:16:06.694Z" }, - { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload_time = "2024-07-15T00:16:09.758Z" }, - { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload_time = "2024-07-15T00:16:11.758Z" }, - { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload_time = "2024-07-15T00:16:13.731Z" }, + { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699, upload-time = "2024-07-15T00:14:04.909Z" }, + { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681, upload-time = "2024-07-15T00:14:13.99Z" }, + { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328, upload-time = "2024-07-15T00:14:16.588Z" }, + { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955, upload-time = "2024-07-15T00:14:19.389Z" }, + { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944, upload-time = "2024-07-15T00:14:22.173Z" }, + { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927, upload-time = "2024-07-15T00:14:24.825Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910, upload-time = "2024-07-15T00:14:26.982Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544, upload-time = "2024-07-15T00:14:29.582Z" }, + { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094, upload-time = "2024-07-15T00:14:40.126Z" }, + { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440, upload-time = "2024-07-15T00:14:42.786Z" }, + { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091, upload-time = "2024-07-15T00:14:45.184Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682, upload-time = "2024-07-15T00:14:47.407Z" }, + { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707, upload-time = "2024-07-15T00:15:03.529Z" }, + { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792, upload-time = "2024-07-15T00:15:28.372Z" }, + { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586, upload-time = "2024-07-15T00:15:32.26Z" }, + { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420, upload-time = "2024-07-15T00:15:34.004Z" }, + { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713, upload-time = "2024-07-15T00:15:35.815Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459, upload-time = "2024-07-15T00:15:37.995Z" }, + { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707, upload-time = "2024-07-15T00:15:39.872Z" }, + { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545, upload-time = "2024-07-15T00:15:41.75Z" }, + { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533, upload-time = "2024-07-15T00:15:44.114Z" }, + { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510, upload-time = "2024-07-15T00:15:46.509Z" }, + { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973, upload-time = "2024-07-15T00:15:49.939Z" }, + { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968, upload-time = "2024-07-15T00:15:52.025Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179, upload-time = "2024-07-15T00:15:54.971Z" }, + { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577, upload-time = "2024-07-15T00:15:57.634Z" }, + { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899, upload-time = "2024-07-15T00:16:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964, upload-time = "2024-07-15T00:16:03.669Z" }, + { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398, upload-time = "2024-07-15T00:16:06.694Z" }, + { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313, upload-time = "2024-07-15T00:16:09.758Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877, upload-time = "2024-07-15T00:16:11.758Z" }, + { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595, upload-time = "2024-07-15T00:16:13.731Z" }, ] From 37fa8dc2bae21b51a874f727b2ba6e499f2e362a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 1 May 2025 20:26:28 -0700 Subject: [PATCH 093/477] Tesla: move steering disengage to generic param (#35097) * add event * fix * for now don't change * bump to master --- cereal/log.capnp | 1 + opendbc_repo | 2 +- selfdrive/car/car_specific.py | 2 ++ selfdrive/selfdrived/events.py | 5 +++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cereal/log.capnp b/cereal/log.capnp index a5828d61cb..6b5398405c 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -48,6 +48,7 @@ struct OnroadEvent @0xc4fa6047f024e718 { preEnableStandstill @12; # added during pre-enable state with brake gasPressedOverride @13; # added when user is pressing gas with no disengage on gas steerOverride @14; + steerDisengage @94; # exits active state cruiseDisabled @15; speedTooLow @16; outOfSpace @17; diff --git a/opendbc_repo b/opendbc_repo index 0d3b96f1db..7b87e9069c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0d3b96f1dbb6d6318b123e6c481248685c805f18 +Subproject commit 7b87e9069c387ee27187efac45cdff42f3378e20 diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index 2d69d3d590..bbecc6e688 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -191,6 +191,8 @@ class CarSpecificEvents: events.add(EventName.accFaulted) if CS.steeringPressed: events.add(EventName.steerOverride) + if CS.steeringDisengage: + events.add(EventName.steerDisengage) if CS.brakePressed and CS.standstill: events.add(EventName.preEnableStandstill) if CS.gasPressed: diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index ee2c418b6e..c187ed474a 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -670,6 +670,11 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { visual_alert=VisualAlert.brakePressed), }, + EventName.steerDisengage: { + ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), + ET.NO_ENTRY: NoEntryAlert("Steering Pressed"), + }, + EventName.preEnableStandstill: { ET.PRE_ENABLE: Alert( "Release Brake to Engage", From e972a47b9389ea172433a7d68a40cc9abfe8952f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 1 May 2025 20:27:14 -0700 Subject: [PATCH 094/477] CI: check submodules after tests (#35100) release: check submodules after tests --- .github/workflows/selfdrive_tests.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 25fc258634..3edb6c2981 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -51,10 +51,6 @@ jobs: timeout-minutes: 1 run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh - uses: ./.github/workflows/setup-with-retry - - name: Check submodules - if: github.repository == 'commaai/openpilot' - timeout-minutes: 3 - run: release/check-submodules.sh - name: Build openpilot and run checks timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache run: | @@ -65,6 +61,10 @@ jobs: run: | cd $STRIPPED_DIR ${{ env.RUN }} "release/check-dirty.sh" + - name: Check submodules + if: github.repository == 'commaai/openpilot' + timeout-minutes: 3 + run: release/check-submodules.sh build: runs-on: From c86204ce6df7207c4d3db8328c0b20cff625cf47 Mon Sep 17 00:00:00 2001 From: dparring <114614741+dparring@users.noreply.github.com> Date: Thu, 1 May 2025 23:28:06 -0500 Subject: [PATCH 095/477] FCA: Ram 1500 improvements (#797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FCA: Ram 1500 improvements * updating reference to my opendbc * syncing latest opendbc * move to car_specific_sp * bump * Realfast ram1500 (#1) * Bump tinygrad to upstream master (#34973) bump tg * test_onroad: test liveParameters service timing (#34977) Add live params to test onroad * selfdrived: remove ignored processes list (#34978) * selfdrived: remove ignored processes list * it's a set * calibrationd: read carParams from Params (#34981) * Read CarParams once from Params * Remove it from process replay * locationd: make pose_kf inherit from KalmanFilter (#34982) * Read message not json for initial state * Delete lines * Fix param * MADS: prevent `wrongCarMode` from disabling MADS enabled state (#792) * MADS: prevent `wrongCarMode` from disabling MADS enabled state Add wrongCarModeNoEntry as a NO_ENTRY event to replace wrongCarMode (USER_DISABLE) that would incorrectly disable MADS when in the wrong car mode. Provides contextual guidance to users based on car brand, showing appropriate instructions for re-engagement without requiring MADS to be re-enabled. * move down * warning only? * cereal SIKE * remove duplicate * try this out * final * Readme: update with links to new branches and info about rewrite and reflash agnos (#795) * Adding the new branches info and adding a warning to reflash agnos * Using more blockquote * it --------- Co-authored-by: Stupefacient * agnos 11.13 (#34980) * agnos 11.12 * new build * Multilang: Update pt-BR translation (#34983) * [bot] Update Python packages (#34987) Update Python packages Co-authored-by: Vehicle Researcher * Multilang: Update ja translation (#34986) * 日本語訳2025/3/19 * numerusform追加。 * connect to an unmetered networkに対応 * panda是正 * opendbc_repo是正 * Multilang: Update zh translation (#34985) * update to latest userdata partition (#34990) update * paramsd: cache backwards compatibility (#34991) * LiveParametersV2 key * Param key * Fix tests * Remove old params if error * Update exception message * Update the test * Add test for corrupted byte format in v1 * Rename params_reader => params * hardwared: give stuck LTE connections a kick This reverts commit e3e694096a3a79e663712797e559e729e6818c7c. * Revert "hardwared: give stuck LTE connections a kick" This reverts commit e4aa346259d9be4713c0d6b7bcd49ac88c277c1f. * process_replay: fix GM and replace TOYOTA3 (#34996) * Add volt and bolt param migration * Remove mazda due to invalid sensor timings * Fix the import * Add mazda to excluded * Bring back mazda, but add todo * New segment for TOYOTA3 * Fix * Update ref commit * bump opendbc * test_models: update relay malfunction check (#34999) relay is checked in fwd hook * process_replay: remove GM (volt) (#35000) Remove volt segment * Revert "test_models: update relay malfunction check" (#35002) Revert "test_models: update relay malfunction check (#34999)" This reverts commit 2cd0079abef0f654541dfc894e97b02829163577. * Reapply "Online lateral lag learning" (#34975) * Online lateral lag learning (#34974) This reverts commit b4cc9e68d126c2219a7ddf33519e222fe8bfe00d. * pad to the best size for fft * Fix static analysis * Add typing * Fix typing * MAX_LAG * Calculate cross correlation regardless if the points are valid * Back to lagd * Add lagd to process_config * Lagd in test onroad * Move lag estimator for lagd * Remove duplicate entry from test_onroad * Update process replay * pre-fill the data * Update cpu usage * 25sec window * Change the meaning of lateralDelayEstimate * No newline * Fix typing * Prefill * Update ref commit * Add a unit test * Fix static issues * Time limit * Or timeout * Use mocker * Update estimate every time * empty test * DT const * enable RIVIAN again * Update ref commit * Update that again * Improve the tests * Fix static * Add masking test * Increase timeout * Add liveDelay to selfdrived * Add liveDelay to selfdrived in process_replay * Fix block_avg restore after num_blocks * regen most * Update bolt * Update ref commit * Change the key name * Add assert * True weighted average * Raise thermal setpoint (#35008) We don't need to control all the way down to 70C * UI: Update `AbstractControlSP_SELECTOR` and `OptionControlSP` (#800) * controls * Adjust label width dynamically based on layout type. Updated the label's fixed width to be conditional on the layout type, improving adaptability for different inline layouts. Additionally, corrected indentation in the width calculation loop for consistency. * Refactor OptionControlSP to improve parameter value handling and encapsulate logic in dedicated methods * Refactor getParamValue to return an integer and ensure value is updated correctly in button click handler * Trying to unify a bit the logic. still WIP * Reducing a bit the change footprint * Refactor spacing item handling to prevent duplicate insertion and improve layout management --------- Co-authored-by: DevTekVE * bump panda * Radard: vision-based yRel for Hyundai single-lead systems (#805) Introduced processing for custom yRel values using HyundaiFlagsSP when the enhanced SCC flag is enabled. Updated `radard` to handle `CarParamsSP` and make necessary adjustments for Hyundai vehicles with specific SCC configurations. * MADS: transition to soft-disable for specific gear states (#791) * MADS: transition to soft-disable for specific gear states This commit enhances the condition checking functionality of the Modular Assistive Driving System (MADS) to implement a 'soft-disable' feature during the vehicle's active motion when a non-forward drive gear is engaged. It includes utilizing structs to reference various car state attributes and modifying a function definition to improve clarity. This adjustment boosts the system's reaction to gear shifts, increasing the safety and efficiency of the driving assist system. * structs * UI: Device & Sunnylink Panels - Standardize push button size & alignment (#806) * layout adjustments * sunnylink_panel --------- Co-authored-by: Jason Wen * [bot] Update Python packages (#35012) * Update Python packages * update refs --------- Co-authored-by: Vehicle Researcher Co-authored-by: Shane Smiskol * ci: Remove redundant Panda build step from prebuilt workflow (#810) The Panda build step was unnecessary as it is not utilized in this workflow. This change simplifies the workflow and reduces redundant actions, improving efficiency. * Device: Customizable Max Time Offroad (#796) * Max Time Offroad * Refactor & Fix param * Error Handling * rename SP variable * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE * Update selfdrive/ui/sunnypilot/qt/widgets/controls.h Co-authored-by: DevTekVE * Update selfdrive/ui/sunnypilot/qt/widgets/controls.cc Co-authored-by: DevTekVE * Update selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/max_time_offroad.h Co-authored-by: DevTekVE * Update selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/max_time_offroad.cc Co-authored-by: DevTekVE * UI layout changes for better alignment selector is not big enough -> make it bigger ;) OptionControlSP now includes a QMap argument to allow actual values to be set in param directly * Rebase & resolve reviews * change default to be closer to OP default * me dumb * MaxTimeOffroad: Add support for 30h limit and improve label formatting * power_monitoring: Refactor MaxTimeOffroad parameter handling for clarity * test: Add unit tests for MaxTimeOffroad parameter handling * power_monitoring: Update MaxTimeOffroad handling to use seconds and improve shutdown logic * power_monitoring: Improve exception handling and remove redundant shutdown check for MaxTimeOffroad --------- Co-authored-by: DevTekVE Co-authored-by: Jason Wen * Hyundai longitudinal: Parse lead info for camera-based SCC platforms (#809) * Hyundai longitudinal: Parse lead info for camera-based SCC platforms * fix * update * bump * update tests * lol why is this here * bump --------- Co-authored-by: DevTekVE Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> * Models: point to v2 of driving models json for recompiled models (#817) Use newer model version json since they have been recompiled * ci: update squash script for external contributors (#811) * Get head ref info * Simpify adding the comment with the newest flagg by gh cli * Dynamic origin * Enhance PR processing: add label handling for forked PRs and improve variable naming * Refactor PR label handling: use constant for trust-fork label and improve comments * Fix remote addition in PR processing: change subprocess call to not check for errors * Refactor PR comment handling: support multiple comments and improve clarity * Refactor PR processing: streamline comment handling and improve error management * Refactor add_pr_comments function: specify comments type as list of strings * Maybe we don't prevent "behind" branches to be merged as lon as the PR itself is mergeable * lint, leave me alone --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> * sunnylink: fix sunnylink backup restore version parsing (#816) * improvement * fix: Improve version parsing logic for sunnypilot --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen * DM: Enforce monitoring of MADS state in driver monitoring (#818) * DM: Enforce monitoring of MADS state in driver monitoring Added `selfdriveStateSP.mads.enabled` to track MADS engagement. This ensures monitoring logic accounts for MADS alongside the existing selfdrive state checks, improving feature integration and event handling. * how * [bot] Update Python packages (#35017) Update Python packages Co-authored-by: Vehicle Researcher * Rename experimental long (#35011) * now alpha long * fix * rename param * thought i did this * debug * debug * more * more * moar * Revert "moar" This reverts commit e347106246583982e1be206485ad4d8eb944f817. * Revert "more" This reverts commit 0746e6d2e400ce1715614c62af84a9680c4d65c1. * Revert "more" This reverts commit 0db5a8792c01bb2d687f9152e6ba37fb76d4a453. * Revert "debug" This reverts commit 72f235b13727980e4331ff487998ddc7c7f7fb63. * Revert "debug" This reverts commit 7871b69f8c1bdffed386c2baac99542fcc50f7eb. * Temporary disable UI preview from CI (#35018) disable * Update Python packages (#819) * Fix possible CAN ignition overlap due to incorrect counter tracking (#35019) * fix-rivian-ign * bump * UI: Bugfix Max Time Offroad (#820) bugfixes Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> * CI: Improve `dev-c3-new` auto build (#825) * force commit to force PR * ci: add job to manage PR labels and remove trust label on new commits * Add pull request trigger * empty commit to test * ci: update pull request label management logic and remove FORCE_RUN flag * ci: update trust label management to use TRUST_FORK_PR_LABEL * ci: update workflow to manage PR labels and adjust trigger conditions * ci: rename jobs in workflow for consistency and clarity * ci: add unleash-nightly-squash branch to workflow * ci: add checkout step with GITHUB_TOKEN to workflow * force wait * what happens if negated the wait * ci: add 'Wait for Tests' action to monitor workflow execution * ci: update wait-for-tests action conditions and cleanup * ci: refine conditions for managing PR labels and waiting for tests * ci: enhance PR label conditions for workflow execution * Cant use env on if * maybe * ci: update pull request workflow conditions and cleanup * Missing end of line * ci: rename workflow and update job name for clarity * Cleaning * ci: refine conditions for pull request handling in workflow * ci: update handling of TRUST_FORK_LABEL in PR processing * ci: remove fork trust warning from PR processing --------- Co-authored-by: Jason Wen * CI: Update PR list query to exclude draft pull requests (#829) * lagd: check sensors valid (#35027) * Check if sensors valid * Fix test * Fix static * Constants * time buffer for pose valid * Fix static * Revert "DM: Enforce monitoring of MADS state in driver monitoring" (#830) Revert "DM: Enforce monitoring of MADS state in driver monitoring (#818)" This reverts commit 3615b1a20371c8e074f591030fb56c770db7c08c. * DM: Enforce monitoring of MADS state in driver monitoring (#831) * DM: Enforce monitoring of MADS state in driver monitoring Added `selfdriveStateSP.mads.enabled` to track MADS engagement. This ensures monitoring logic accounts for MADS alongside the existing selfdrive state checks, improving feature integration and event handling. * how * okay, only when active * ui(raylib): update spinner progress bar to match Qt (#35028) * lagd: estimate std (#35009) * Std * Fix static * Refactor * Assert std zero * update * ui(raylib): fix typos (#35030) * Update modeld action logic (#35032) * add action logic * magic numbers * unused * plot accel * more changes * lagd: check for validity of the estimate when restoring state (#35034) Do not restore if invalid * Multilang: Update de translation (#35025) added missing German translations * Tomb raider 2 (#35029) * db56b8fb-9135-4ab6-af18-99b7df7b2245/400 * fixes * linter unhappy * 6dbe0991-baa1-49ad-836a-ab370d1f0d92/400 * This one is good: 19387087-1005-475e-9015-9458dd8e7c5f/400 * Better every day: 39ed911c-0937-417f-97d2-58a8bb3caa53/400 * Actually end-to-end * typo * smooooooth: 94e23541-eb84-4fef-9f51-6a2d82aff314/360 * Revert "smooooooth: 94e23541-eb84-4fef-9f51-6a2d82aff314/360" This reverts commit edd4f02386d83d82dd8a188985cde80ed1646b7f. * 11632ef7-f555-489c-8480-e3bf97d9285e/400 * 08712d27-f6bd-4536-a30e-c729e5f62356/400 * 0a92a35e-1f72-476a-8cb6-c9f103f36822/400 * ee6d2394-2072-420c-a664-b4c0d4ed0b61/400 * no prev curv * No double work * fix bug * smooth * update prev action * whitespace * add little accel * new ref * Update plant.py * DEC: adjust dynamic speed adaptation parameters (#813) * early and better * Update sunnypilot/selfdrive/controls/lib/dec/constants.py --------- Co-authored-by: Jason Wen Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> * OS04C10: use IFE downscaler for road cameras (#35023) * squashh * wrong * clean up * rename * process replay migration: fix longitudinalPlan (#35035) * ci: ui preview add unicode characters and emojis (#835) Add unicode characters and emojis! * ui(raylib): update text window font, new lines and indentation (#35031) * NNLC: decreased low-speed factor (#822) * NNLC: decreased low-speed factor * np.float to float * format * add tests for sanity check --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen * ui(raylib): calculate spinner progress in set_text (#35036) * bugfix: Auto lane change - Add mode to disable lane changes (#826) * off is off * sugg * line * Update sunnypilot/selfdrive/controls/lib/tests/test_auto_lane_change.py --------- Co-authored-by: Jason Wen Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: DevTekVE * NNLC: use `safe_exp` to prevent overflow in `sigmoid` (#836) * test * prevent overflowing * unused * NNLC: fix weak torque in high lat-accel turns (#823) * NNLC: fix weak torque in high lat-accel turns * np.float to float --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen * ci: trigger prebuilt builds via label from PR * ci: enhance pull request handling for prebuilt workflows * ci: add wait-for-action step to monitor selfdrive_tests workflow * ci: add validation job to monitor selfdrive_tests for prebuilt workflows * test * force negative condition to validate flow * force negative condition to validate flow * ci: add wait_for_tests input to control selfdrive_tests workflow execution * ci: update description for wait_for_tests input in workflow * Cleaning * Remove PR label when triggered by the label * Rename * Changing to target and only caring about labeled for the time being * gh action needs cancelled not canceled * Line ending * ci: avoid deadlock on dev-c3 branch build when PR labeled dev-c3 (#839) * ci: enhance PR checks to validate individual check runs before merging and ignore reset-and-squash as candidate for fail * reset and squash script shouldn't will be cancelled only if a push to master has been made which always should take priority. The rest will be put on hold to avoid parallel squash scripts running * Set concurrencty at workflow level? * Set concurrencty at workflow level? * change trigger to pull_request to validate * Reducing the types of "pull request" events * Keep it as target * Playing eith run name a little * Change to pull request not target * simplifying * set to just pull request without target * maintain as pull request target * ci: update auto pr review config and split turst fork pr (#842) * Updating auto pr labeler * auto pr review revamp * Leaving event as pull request :) * Check permission * add chore * Bringing back as original * ci: bugfix flipped contains check for pull requests * ci: bugfix cancelling only when push to master or workflow dispatch. Otherwise don't let it cancel as it seems that when running with pull_request_target we are with ref pointing to target and thus the cancel condition was being met * ui(raylib): constant spinner rotation speed (#35037) * ci: improve conditions for publish and notify steps in workflow (#844) * [bot] Update translations (#35040) Update translations Co-authored-by: Vehicle Researcher * Long planner: make work with training (#35043) * revert useless * update ref * Modeld: small refactor (#35044) refactor * update smooth * raylib: init updater (#35045) * raylib: init updater * cleanup * Revert "OS04C10: use IFE downscaler for road cameras" (#35046) Revert "OS04C10: use IFE downscaler for road cameras (#35023)" This reverts commit 3b60b22ceeb090812e5fa948ac6e56442d681dc4. * Tomb Raider 3 (#35042) * 92b64884-4506-4a03-87ad-33e1a177fe73/400 * faster lat * DM: Use `carControl.latActive` for MADS DM monitoring (#845) * ci: fixes for trigger conditions on sync (#847) pull_request_target is needed for security, but at the same time it makes life a little harder because the ref is different so we need to do some work to make sure we get what we are supposed to get * ui(raylib): reduce spinner rotation artifact (#35048) * ui(raylib): reduce spinner rotation artifact A visual artifact (white pixels) appeared on the edge of the rotating spinner track texture, likely due to RGB color bleed during bilinear filtering in Raylib. Pre-multiplying the alpha channel of the spinner track image using `rl.image_alpha_premultiply` significantly reduces the visibility of the artifact. * lint * ui(raylib): increase font size and wrap text in spinner (#35049) - Wrap text onto separate lines - Increase font size to be closer to Qt - Remove extra letter spacing - 0.0 should use font default spacing, and this is used in `wrap_text` Will fix vertical alignment separately, as both the text and progress bar layouts need to be considered * ui(raylib): update spinner vertical pos (#35051) Adjust the spinner vertical position when displaying text or a progress bar - When displaying the progress bar, center the comma logo and spinner in the middle of the screen - When displaying text, center the entire content vertically Also updated `wrap_text` to not include an empty line in the array if it's the first line, so that `wrap_text("")` always returns `[]` * ui(raylib): "exit" btn in text window on PC (#35052) * ui(raylib): "exit" btn in text window on PC * unused import * Revert "ui(raylib): "exit" btn in text window on PC (#35052)" This reverts commit f9767d265010101f123dcdc261206cb7be8bb5d8. * plotjuggler: support relative paths (#35054) * Support relative paths * Abspath for rlog too * Reapply "ui(raylib): "exit" btn in text window on PC" (#35053) * ui(raylib): "exit" btn in text window on PC * unused import * request close * this was missing * Disable low speed steer in modeld (#35056) * disable low speed steer * No steer under * TOYOTA_RAV4_PRIME NNLC tuning gen 1 (#850) neural_network_data: TOYOTA_RAV4_PRIME NNLC tuning gen 1 * SL: updating localproxy implementation (#841) * Adding capabilities to route localProxy via sunnylink * Undo * Thx lint * get api token * cert is not valid when it's an IP. Still use cert, but don't validate * move to car_specific_sp * bump --------- Co-authored-by: Harald Schäfer Co-authored-by: Kacper Rączy Co-authored-by: Adeeb Shihadeh Co-authored-by: Jason Wen Co-authored-by: DevTekVE Co-authored-by: Stupefacient Co-authored-by: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Co-authored-by: commaci-public <60409688+commaci-public@users.noreply.github.com> Co-authored-by: Vehicle Researcher Co-authored-by: programanichiro <99449198+programanichiro@users.noreply.github.com> Co-authored-by: eFini Co-authored-by: Maxime Desroches Co-authored-by: Shane Smiskol Co-authored-by: Robbe Derks Co-authored-by: Nayan Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Cameron Clough Co-authored-by: niko001 Co-authored-by: Kumar <36933347+rav4kumar@users.noreply.github.com> Co-authored-by: ZwX1616 Co-authored-by: YassineYousfi Co-authored-by: Tim Wilson Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Warren Togami * fixing missing event * more event fixes * fixing spacing * more fixes * revert * update * update * update * reverting unneeded event * S P A C I N G * bumping opendbc * bump * Update sunnypilot/selfdrive/car/car_specific.py * Update sunnypilot/selfdrive/car/car_specific.py --------- Co-authored-by: Jason Wen Co-authored-by: Harald Schäfer Co-authored-by: Kacper Rączy Co-authored-by: Adeeb Shihadeh Co-authored-by: DevTekVE Co-authored-by: Stupefacient Co-authored-by: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Co-authored-by: commaci-public <60409688+commaci-public@users.noreply.github.com> Co-authored-by: Vehicle Researcher Co-authored-by: programanichiro <99449198+programanichiro@users.noreply.github.com> Co-authored-by: eFini Co-authored-by: Maxime Desroches Co-authored-by: Shane Smiskol Co-authored-by: Robbe Derks Co-authored-by: Nayan Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Cameron Clough Co-authored-by: niko001 Co-authored-by: Kumar <36933347+rav4kumar@users.noreply.github.com> Co-authored-by: ZwX1616 Co-authored-by: YassineYousfi Co-authored-by: Tim Wilson Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Warren Togami --- opendbc_repo | 2 +- selfdrive/selfdrived/selfdrived.py | 2 +- sunnypilot/selfdrive/car/car_specific.py | 31 ++++++++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 63fbea047b..ac089b6314 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 63fbea047be715cb772701270e66eddda49433a4 +Subproject commit ac089b63147450275e2dd3d261463ff992271d6a diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 36d1b27044..0b825869f4 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -198,7 +198,7 @@ class SelfdriveD(CruiseHelper): car_events = self.car_events.update(CS, self.CS_prev, self.sm['carControl']).to_msg() self.events.add_from_msg(car_events) - car_events_sp = self.car_events_sp.update().to_msg() + car_events_sp = self.car_events_sp.update(CS, self.events).to_msg() self.events_sp.add_from_msg(car_events_sp) if self.CP.notCar: diff --git a/sunnypilot/selfdrive/car/car_specific.py b/sunnypilot/selfdrive/car/car_specific.py index 057f79cf0c..649aceca01 100644 --- a/sunnypilot/selfdrive/car/car_specific.py +++ b/sunnypilot/selfdrive/car/car_specific.py @@ -5,12 +5,16 @@ This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ -from cereal import custom +from cereal import log, custom from opendbc.car import structs +from opendbc.car.chrysler.values import RAM_DT +from openpilot.selfdrive.selfdrived.events import Events from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP +EventName = log.OnroadEvent.EventName EventNameSP = custom.OnroadEventSP.EventName +GearShifter = structs.CarState.GearShifter class CarSpecificEventsSP: @@ -18,6 +22,7 @@ class CarSpecificEventsSP: self.CP = CP self.params = params + self.low_speed_alert = False self.hyundai_radar_tracks = self.params.get_bool("HyundaiRadarTracks") self.hyundai_radar_tracks_confirmed = self.params.get_bool("HyundaiRadarTracksConfirmed") @@ -25,10 +30,26 @@ class CarSpecificEventsSP: self.hyundai_radar_tracks = self.params.get_bool("HyundaiRadarTracks") self.hyundai_radar_tracks_confirmed = self.params.get_bool("HyundaiRadarTracksConfirmed") - def update(self): - events = EventsSP() + def update(self, CS: structs.CarState, events: Events): + events_sp = EventsSP() + + if self.CP.brand == 'chrysler': + if self.CP.carFingerprint in RAM_DT: + # remove belowSteerSpeed event from CarSpecificEvents as RAM_DT uses a different logic + if events.has(EventName.belowSteerSpeed): + events.remove(EventName.belowSteerSpeed) + + # TODO-SP: use if/elif to have the gear shifter condition takes precedence over the speed condition + # TODO-SP: add 1 m/s hysteresis + if CS.vEgo >= self.CP.minEnableSpeed: + self.low_speed_alert = False + if CS.gearShifter != GearShifter.drive: + self.low_speed_alert = True + if self.low_speed_alert: + events.add(EventName.belowSteerSpeed) + if self.CP.brand == 'hyundai': if self.hyundai_radar_tracks and not self.hyundai_radar_tracks_confirmed: - events.add(EventNameSP.hyundaiRadarTracksConfirmed) + events_sp.add(EventNameSP.hyundaiRadarTracksConfirmed) - return events + return events_sp From b58552542d29bce3c8ae17a2c940dbf935197aed Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 1 May 2025 22:10:16 -0700 Subject: [PATCH 096/477] Tesla safety: fix high angle rate fault and enforce steering disengage via safety (#35101) --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 7b87e9069c..6af5ad8dd5 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 7b87e9069c387ee27187efac45cdff42f3378e20 +Subproject commit 6af5ad8dd5de3b890d2812cc19b063caae858b31 From b4c3e4d4d9b841a40f5ae43d5759bb2dc1bdc37b Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Fri, 2 May 2025 16:25:23 +0200 Subject: [PATCH 097/477] models: Remove local model compilation for modeldv2 (#868) * Remove supercombo model ONNX file. Deleted the large `supercombo.onnx` model file from the repository. This cleanup reduces repository size and dependency on unused or outdated files for this version. * Disable tinygrad model compilation on macos temporarily * Remove unused dmonitoring model file. Deleted the ONNX model for dmonitoring as it is no longer required. This eliminates unnecessary assets and reduces repository size. * Removing the model also from the snpe build, we have them, prebuilt --- selfdrive/modeld/SConscript | 10 +- sunnypilot/modeld/SConscript | 12 -- sunnypilot/modeld/models/supercombo.onnx | 3 - sunnypilot/modeld_v2/SConscript | 18 -- sunnypilot/modeld_v2/dmonitoringmodeld | 4 - sunnypilot/modeld_v2/dmonitoringmodeld.py | 192 ------------------ .../models/dmonitoring_model.current | 2 - .../modeld_v2/models/dmonitoring_model.onnx | 3 - sunnypilot/modeld_v2/models/supercombo.onnx | 3 - 9 files changed, 6 insertions(+), 241 deletions(-) delete mode 100644 sunnypilot/modeld/models/supercombo.onnx delete mode 100755 sunnypilot/modeld_v2/dmonitoringmodeld delete mode 100755 sunnypilot/modeld_v2/dmonitoringmodeld.py delete mode 100644 sunnypilot/modeld_v2/models/dmonitoring_model.current delete mode 100644 sunnypilot/modeld_v2/models/dmonitoring_model.onnx delete mode 100644 sunnypilot/modeld_v2/models/supercombo.onnx diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index cecebfa18b..0c04ec2823 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -47,8 +47,10 @@ elif arch == 'Darwin': else: device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0' -for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: - fn = File(f"models/{model_name}").abspath - cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' - lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd) +# TODO-SP: after 15.4 it's not possible to compile models locally on mac https://discord.com/channels/469524606043160576/1362735424644055230 +if arch != 'Darwin': + for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: + fn = File(f"models/{model_name}").abspath + cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' + lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd) diff --git a/sunnypilot/modeld/SConscript b/sunnypilot/modeld/SConscript index 1a54fe1e32..bebd2de953 100644 --- a/sunnypilot/modeld/SConscript +++ b/sunnypilot/modeld/SConscript @@ -52,15 +52,3 @@ commonmodel_lib = lenv.Library('commonmodel', common_src) lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks) lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath) lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks) - -tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath)] - -# Get model metadata -fn = File("models/supercombo").abspath -cmd = f'python3 {Dir("#sunnypilot/modeld").abspath}/get_model_metadata.py {fn}.onnx' -lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files, cmd) - -if arch == "larch64": - thneed_lib = env.SharedLibrary('thneed', thneed_src, LIBS=[gpucommon, common, 'OpenCL', 'dl']) - thneedmodel_lib = env.Library('thneedmodel', ['runners/thneedmodel.cc']) - lenvCython.Program('runners/thneedmodel_pyx.so', 'runners/thneedmodel_pyx.pyx', LIBS=envCython["LIBS"]+[thneedmodel_lib, thneed_lib, gpucommon, common, 'dl', 'OpenCL']) diff --git a/sunnypilot/modeld/models/supercombo.onnx b/sunnypilot/modeld/models/supercombo.onnx deleted file mode 100644 index 8b7126c44a..0000000000 --- a/sunnypilot/modeld/models/supercombo.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb2018c74cdd9e5cb070ec7bed7f8581fabd55e39057d0a03aaffd2e42408154 -size 62486347 diff --git a/sunnypilot/modeld_v2/SConscript b/sunnypilot/modeld_v2/SConscript index 1a173dcdeb..4ade1469f0 100644 --- a/sunnypilot/modeld_v2/SConscript +++ b/sunnypilot/modeld_v2/SConscript @@ -29,22 +29,4 @@ for pathdef, fn in {'TRANSFORM': 'transforms/transform.cl', 'LOADYUV': 'transfor cython_libs = envCython["LIBS"] + libs commonmodel_lib = lenv.Library('commonmodel', common_src) lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks) -tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x] - -# Get model metadata -fn = File("models/supercombo").abspath -cmd = f'python3 {Dir("#sunnypilot/modeld_v2").abspath}/get_model_metadata.py {fn}.onnx' -lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files, cmd) - -# Compile tinygrad model -pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"' -if arch == 'larch64': - device_string = 'QCOM=1' -else: - device_string = 'CLANG=1 IMAGE=0' - -for model_name in ['supercombo', 'dmonitoring_model']: - fn = File(f"models/{model_name}").abspath - cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' - lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd) diff --git a/sunnypilot/modeld_v2/dmonitoringmodeld b/sunnypilot/modeld_v2/dmonitoringmodeld deleted file mode 100755 index 90b43800fe..0000000000 --- a/sunnypilot/modeld_v2/dmonitoringmodeld +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -exec "$DIR/dmonitoringmodeld.py" "$@" diff --git a/sunnypilot/modeld_v2/dmonitoringmodeld.py b/sunnypilot/modeld_v2/dmonitoringmodeld.py deleted file mode 100755 index 4469618c88..0000000000 --- a/sunnypilot/modeld_v2/dmonitoringmodeld.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env python3 -import os -from openpilot.system.hardware import TICI -if TICI: - from tinygrad.tensor import Tensor - from tinygrad.dtype import dtypes - from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address - os.environ['QCOM'] = '1' -else: - from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner -import math -import time -import pickle -import ctypes -import numpy as np -from pathlib import Path -from setproctitle import setproctitle - -from cereal import messaging -from cereal.messaging import PubMaster, SubMaster -from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf -from openpilot.common.swaglog import cloudlog -from openpilot.common.realtime import config_realtime_process -from openpilot.common.transformations.model import dmonitoringmodel_intrinsics, DM_INPUT_SIZE -from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye -from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import CLContext, MonitoringModelFrame -from openpilot.sunnypilot.modeld_v2.parse_model_outputs import sigmoid -from openpilot.system import sentry - -MODEL_WIDTH, MODEL_HEIGHT = DM_INPUT_SIZE -CALIB_LEN = 3 -FEATURE_LEN = 512 -OUTPUT_SIZE = 84 + FEATURE_LEN - -PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld" -SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') -MODEL_PATH = Path(__file__).parent / 'models/dmonitoring_model.onnx' -MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl' - - -class DriverStateResult(ctypes.Structure): - _fields_ = [ - ("face_orientation", ctypes.c_float*3), - ("face_position", ctypes.c_float*3), - ("face_orientation_std", ctypes.c_float*3), - ("face_position_std", ctypes.c_float*3), - ("face_prob", ctypes.c_float), - ("_unused_a", ctypes.c_float*8), - ("left_eye_prob", ctypes.c_float), - ("_unused_b", ctypes.c_float*8), - ("right_eye_prob", ctypes.c_float), - ("left_blink_prob", ctypes.c_float), - ("right_blink_prob", ctypes.c_float), - ("sunglasses_prob", ctypes.c_float), - ("occluded_prob", ctypes.c_float), - ("ready_prob", ctypes.c_float*4), - ("not_ready_prob", ctypes.c_float*2)] - - -class DMonitoringModelResult(ctypes.Structure): - _fields_ = [ - ("driver_state_lhd", DriverStateResult), - ("driver_state_rhd", DriverStateResult), - ("poor_vision_prob", ctypes.c_float), - ("wheel_on_right_prob", ctypes.c_float), - ("features", ctypes.c_float*FEATURE_LEN)] - - -class ModelState: - inputs: dict[str, np.ndarray] - output: np.ndarray - - def __init__(self, cl_ctx): - assert ctypes.sizeof(DMonitoringModelResult) == OUTPUT_SIZE * ctypes.sizeof(ctypes.c_float) - - self.frame = MonitoringModelFrame(cl_ctx) - self.numpy_inputs = { - 'calib': np.zeros((1, CALIB_LEN), dtype=np.float32), - } - - if TICI: - self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()} - with open(MODEL_PKL_PATH, "rb") as f: - self.model_run = pickle.load(f) - else: - self.onnx_cpu_runner = make_onnx_cpu_runner(MODEL_PATH) - - def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple[np.ndarray, float]: - self.numpy_inputs['calib'][0,:] = calib - - t1 = time.perf_counter() - - input_img_cl = self.frame.prepare(buf, transform.flatten()) - if TICI: - # The imgs tensors are backed by opencl memory, only need init once - if 'input_img' not in self.tensor_inputs: - self.tensor_inputs['input_img'] = qcom_tensor_from_opencl_address(input_img_cl.mem_address, (1, MODEL_WIDTH*MODEL_HEIGHT), dtype=dtypes.uint8) - else: - self.numpy_inputs['input_img'] = self.frame.buffer_from_cl(input_img_cl).reshape((1, MODEL_WIDTH*MODEL_HEIGHT)) - - if TICI: - output = self.model_run(**self.tensor_inputs).numpy().flatten() - else: - output = self.onnx_cpu_runner.run(None, self.numpy_inputs)[0].flatten() - - t2 = time.perf_counter() - return output, t2 - t1 - - -def fill_driver_state(msg, ds_result: DriverStateResult): - msg.faceOrientation = list(ds_result.face_orientation) - msg.faceOrientationStd = [math.exp(x) for x in ds_result.face_orientation_std] - msg.facePosition = list(ds_result.face_position[:2]) - msg.facePositionStd = [math.exp(x) for x in ds_result.face_position_std[:2]] - msg.faceProb = float(sigmoid(ds_result.face_prob)) - msg.leftEyeProb = float(sigmoid(ds_result.left_eye_prob)) - msg.rightEyeProb = float(sigmoid(ds_result.right_eye_prob)) - msg.leftBlinkProb = float(sigmoid(ds_result.left_blink_prob)) - msg.rightBlinkProb = float(sigmoid(ds_result.right_blink_prob)) - msg.sunglassesProb = float(sigmoid(ds_result.sunglasses_prob)) - msg.occludedProb = float(sigmoid(ds_result.occluded_prob)) - msg.readyProb = [float(sigmoid(x)) for x in ds_result.ready_prob] - msg.notReadyProb = [float(sigmoid(x)) for x in ds_result.not_ready_prob] - - -def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts: int, execution_time: float, gpu_execution_time: float): - model_result = ctypes.cast(model_output.ctypes.data, ctypes.POINTER(DMonitoringModelResult)).contents - msg = messaging.new_message('driverStateV2', valid=True) - ds = msg.driverStateV2 - ds.frameId = frame_id - ds.modelExecutionTime = execution_time - ds.gpuExecutionTime = gpu_execution_time - ds.poorVisionProb = float(sigmoid(model_result.poor_vision_prob)) - ds.wheelOnRightProb = float(sigmoid(model_result.wheel_on_right_prob)) - ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b'' - fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd) - fill_driver_state(ds.rightDriverData, model_result.driver_state_rhd) - return msg - - -def main(): - setproctitle(PROCESS_NAME) - config_realtime_process([0, 1, 2, 3], 5) - - sentry.set_tag("daemon", PROCESS_NAME) - cloudlog.bind(daemon=PROCESS_NAME) - - cl_context = CLContext() - model = ModelState(cl_context) - cloudlog.warning("models loaded, dmonitoringmodeld starting") - - cloudlog.warning("connecting to driver stream") - vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True, cl_context) - while not vipc_client.connect(False): - time.sleep(0.1) - assert vipc_client.is_connected() - cloudlog.warning(f"connected with buffer size: {vipc_client.buffer_len}") - - sm = SubMaster(["liveCalibration"]) - pm = PubMaster(["driverStateV2"]) - - calib = np.zeros(CALIB_LEN, dtype=np.float32) - model_transform = None - - while True: - buf = vipc_client.recv() - if buf is None: - continue - - if model_transform is None: - cam = _os_fisheye if buf.width == _os_fisheye.width else _ar_ox_fisheye - model_transform = np.linalg.inv(np.dot(dmonitoringmodel_intrinsics, np.linalg.inv(cam.intrinsics))).astype(np.float32) - - sm.update(0) - if sm.updated["liveCalibration"]: - calib[:] = np.array(sm["liveCalibration"].rpyCalib) - - t1 = time.perf_counter() - model_output, gpu_execution_time = model.run(buf, calib, model_transform) - t2 = time.perf_counter() - - pm.send("driverStateV2", get_driverstate_packet(model_output, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, gpu_execution_time)) - - -if __name__ == "__main__": - try: - main() - except KeyboardInterrupt: - cloudlog.warning(f"child {PROCESS_NAME} got SIGINT") - except Exception: - sentry.capture_exception() - raise diff --git a/sunnypilot/modeld_v2/models/dmonitoring_model.current b/sunnypilot/modeld_v2/models/dmonitoring_model.current deleted file mode 100644 index 121871ef2b..0000000000 --- a/sunnypilot/modeld_v2/models/dmonitoring_model.current +++ /dev/null @@ -1,2 +0,0 @@ -fa69be01-b430-4504-9d72-7dcb058eb6dd -d9fb22d1c4fa3ca3d201dbc8edf1d0f0918e53e6 diff --git a/sunnypilot/modeld_v2/models/dmonitoring_model.onnx b/sunnypilot/modeld_v2/models/dmonitoring_model.onnx deleted file mode 100644 index dcc727510e..0000000000 --- a/sunnypilot/modeld_v2/models/dmonitoring_model.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:50efe6451a3fb3fa04b6bb0e846544533329bd46ecefe9e657e91214dee2aaeb -size 7196502 diff --git a/sunnypilot/modeld_v2/models/supercombo.onnx b/sunnypilot/modeld_v2/models/supercombo.onnx deleted file mode 100644 index 4c9f795574..0000000000 --- a/sunnypilot/modeld_v2/models/supercombo.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d21daa542227ecc5972da45df4e26f018ba113c0461f270e367d57e3ad89221a -size 51461700 From 6151abe08a5abe4397a9a286bce40585725e562d Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Fri, 2 May 2025 18:05:26 +0200 Subject: [PATCH 098/477] ci: Add error handling for failed workflow run (#869) Enhance action to fail on non-successful workflow runs Add logic to check the conclusion of the watched workflow run. If the run ends with a non-successful status, the action now exits with an error to improve error handling and ensure reliability. Co-authored-by: Jason Wen --- .github/workflows/wait-for-action/action.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/wait-for-action/action.yaml b/.github/workflows/wait-for-action/action.yaml index 574ef15186..9cde4cf076 100644 --- a/.github/workflows/wait-for-action/action.yaml +++ b/.github/workflows/wait-for-action/action.yaml @@ -40,5 +40,13 @@ runs: RUN_ID=$(gh run list --workflow=${{ inputs.workflow }} --branch="$BRANCH" --limit=1 --json databaseId --jq '.[0].databaseId') echo "Watching run ID: $RUN_ID" gh run watch "$RUN_ID" + + CONCLUSION=$(gh run view "$RUN_ID" --json conclusion --jq '.conclusion') + echo "Run concluded with: $CONCLUSION" + + if [[ "$CONCLUSION" != "success" ]]; then + echo "❌ Workflow run failed with conclusion: $CONCLUSION" + exit 1 + fi env: GITHUB_TOKEN: ${{ inputs.github-token }} From e1d2360b8c1669ef664f4fc677c7da3023414bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Fri, 2 May 2025 10:17:40 -0700 Subject: [PATCH 099/477] Revert: Tomb Raider 6 (#35105) --- selfdrive/modeld/modeld.py | 7 +++---- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index dde742f159..25bb190fcd 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -26,7 +26,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.selfdrive.modeld.constants import ModelConstants, Plan @@ -54,9 +54,8 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log. ModelConstants.T_IDXS, action_t=long_action_t) desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS) - desired_curvature = get_curvature_from_plan(plan[:, Plan.T_FROM_CURRENT_EULER][:, 2], - plan[:, Plan.ORIENTATION_RATE][:, 2], - ModelConstants.T_IDXS, v_ego, lat_action_t) + + desired_curvature = model_output['desired_curvature'][0, 0] if v_ego > MIN_LAT_CONTROL_SPEED: desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) else: diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 5b9b686829..2075792f4d 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a478376723ba79e2393ca95e2d3e497571ba6fed113e5f13a36f0e4b4d4a7c5 -size 15588463 +oid sha256:b86d130c5db2d772da1b139c136ed86976f37137129a19a6b881fdf641bca198 +size 15578328 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 0a7b4a803d..5e08b78472 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dad289ae367cefcb862ef1d707fb4919d008f0eeaa1ebaf18df58d8de5a7d96e +oid sha256:f222d2c528f1763828de01bb55e8979b1e4056e1dbb41350f521d2d2bb09d177 size 46265585 From a1ec8c6bfe93d5af4a4236291bec1335a874ec24 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 2 May 2025 20:23:30 -0700 Subject: [PATCH 100/477] test models: check steering disengage matches for Tesla (#35107) test models: check steering disengage matches for tesla --- selfdrive/car/tests/test_models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 53f2bb586d..0a4784aca8 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -330,6 +330,7 @@ class TestCarModelBase(unittest.TestCase): prev_panda_gas = self.safety.get_gas_pressed_prev() prev_panda_brake = self.safety.get_brake_pressed_prev() prev_panda_regen_braking = self.safety.get_regen_braking_prev() + prev_panda_steering_disengage = self.safety.get_steering_disengage_prev() prev_panda_vehicle_moving = self.safety.get_vehicle_moving() prev_panda_vehicle_speed_min = self.safety.get_vehicle_speed_min() prev_panda_vehicle_speed_max = self.safety.get_vehicle_speed_max() @@ -357,6 +358,9 @@ class TestCarModelBase(unittest.TestCase): if self.safety.get_regen_braking_prev() != prev_panda_regen_braking: self.assertEqual(CS.regenBraking, self.safety.get_regen_braking_prev()) + if self.safety.get_steering_disengage_prev() != prev_panda_steering_disengage: + self.assertEqual(CS.steeringDisengage, self.safety.get_steering_disengage_prev()) + if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving: self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving()) @@ -432,6 +436,7 @@ class TestCarModelBase(unittest.TestCase): brake_pressed = False checks['brakePressed'] += brake_pressed != self.safety.get_brake_pressed_prev() checks['regenBraking'] += CS.regenBraking != self.safety.get_regen_braking_prev() + checks['steeringDisengage'] += CS.steeringDisengage != self.safety.get_steering_disengage_prev() if self.CP.pcmCruise: # On most pcmCruise cars, openpilot's state is always tied to the PCM's cruise state. From df4f2955dc16325472322522b12a8db5347f52fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Fri, 2 May 2025 20:54:42 -0700 Subject: [PATCH 101/477] Revert TR (#35110) * Revert "Tomb raider 2 (#35029)" This reverts commit 2c162d9b75ae4e77bdf5bc3225e880d352ec0f5c. * bugfix * fix policy * min control speed --- selfdrive/controls/lib/longitudinal_planner.py | 15 +++------------ selfdrive/modeld/fill_model_msg.py | 10 +++++----- selfdrive/modeld/modeld.py | 6 +++--- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- selfdrive/modeld/parse_model_outputs.py | 13 +++++++------ selfdrive/test/process_replay/ref_commit | 2 +- 7 files changed, 23 insertions(+), 31 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 4219e80f33..9d71faeca2 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -90,7 +90,7 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' + self.mpc.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: accel_coast = get_coast_accel(sm['carControl'].orientationNED[1]) @@ -113,7 +113,7 @@ class LongitudinalPlanner: # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - if self.mode == 'acc': + if self.mpc.mode == 'acc': accel_clip = [ACCEL_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP) @@ -160,17 +160,8 @@ class LongitudinalPlanner: self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 action_t = self.CP.longitudinalActuatorDelay + DT_MDL - output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, + output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, action_t=action_t, vEgoStopping=self.CP.vEgoStopping) - output_a_target_e2e = sm['modelV2'].action.desiredAcceleration - output_should_stop_e2e = sm['modelV2'].action.shouldStop - - if self.mode == 'acc': - output_a_target = output_a_target_mpc - self.output_should_stop = output_should_stop_mpc - else: - output_a_target = min(output_a_target_mpc, output_a_target_e2e) - self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc for idx in range(2): accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05) diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 36bd724d01..a91c6395c7 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -90,11 +90,11 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T) # temporal pose - #temporal_pose = modelV2.temporalPose - #temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() - #temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() - #temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() - #temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() + temporal_pose = modelV2.temporalPose + temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() + temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() + temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() + temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() # poly path fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 25bb190fcd..5e1acdea0e 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -41,8 +41,8 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' -LAT_SMOOTH_SECONDS = 0.3 -LONG_SMOOTH_SECONDS = 0.3 +LAT_SMOOTH_SECONDS = 0.0 +LONG_SMOOTH_SECONDS = 0.0 MIN_LAT_CONTROL_SPEED = 0.3 @@ -172,7 +172,7 @@ class ModelState: # TODO model only uses last value now self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] - self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] + self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs] combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} if SEND_RAW_PRED: diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 2075792f4d..3601bbb5da 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b86d130c5db2d772da1b139c136ed86976f37137129a19a6b881fdf641bca198 -size 15578328 +oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36 +size 15966721 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 5e08b78472..aee6d8f1bf 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f222d2c528f1763828de01bb55e8979b1e4056e1dbb41350f521d2d2bb09d177 -size 46265585 +oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624 +size 34882971 diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 783572d436..810c44ccb9 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -88,12 +88,6 @@ class Parser: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) - self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) - for k in ['lead_prob', 'lane_lines_prob']: - self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) return outs @@ -101,10 +95,17 @@ class Parser: def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) if 'lat_planner_solution' in outs: self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) if 'desired_curvature' in outs: self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) + for k in ['lead_prob', 'lane_lines_prob']: + self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 3dd3eab25f..1ae4c4a1a8 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 \ No newline at end of file +7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 From 3f883ad2154c08d5f7428a3ec15351856c7dc573 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 3 May 2025 00:41:04 -0400 Subject: [PATCH 102/477] ui: add longitudinal settings panel (#852) * ui: add longitudinal settings panel * ui preview * add ui preview * rename to Cruise --- selfdrive/ui/sunnypilot/SConscript | 1 + .../qt/offroad/settings/longitudinal_panel.cc | 11 +++++++++++ .../qt/offroad/settings/longitudinal_panel.h | 17 +++++++++++++++++ .../sunnypilot/qt/offroad/settings/settings.cc | 2 ++ selfdrive/ui/tests/test_ui/run.py | 9 ++++++++- 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index deeec44732..4ff3f70477 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -21,6 +21,7 @@ qt_src = [ "sunnypilot/qt/offroad/offroad_home.cc", "sunnypilot/qt/offroad/settings/device_panel.cc", "sunnypilot/qt/offroad/settings/lateral_panel.cc", + "sunnypilot/qt/offroad/settings/longitudinal_panel.cc", "sunnypilot/qt/offroad/settings/max_time_offroad.cc", "sunnypilot/qt/offroad/settings/settings.cc", "sunnypilot/qt/offroad/settings/software_panel.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc new file mode 100644 index 0000000000..9d8a4d9c19 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h" + +LongitudinalPanel::LongitudinalPanel(QWidget *parent) : QWidget(parent) { +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h new file mode 100644 index 0000000000..cd68e3ec2f --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +class LongitudinalPanel : public QWidget { + Q_OBJECT + +public: + explicit LongitudinalPanel(QWidget *parent = nullptr); +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc index 6588c1a6bd..9dd078e52d 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc @@ -16,6 +16,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" @@ -79,6 +80,7 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"), PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"), PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"), + PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/offroad/icon_speed_limit.png"), PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"), PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"), PanelInfo(" " + tr("Firehose"), new FirehosePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_firehose.svg"), diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 54d1889453..064ef14a73 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -239,11 +239,17 @@ def setup_settings_steering_alc(click, pm: PubMaster, scroll=None): click(970, 534) time.sleep(UI_DELAY) -def setup_settings_trips(click, pm: PubMaster, scroll=None): +def setup_settings_driving(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) click(278, 962) time.sleep(UI_DELAY) +def setup_settings_trips(click, pm: PubMaster, scroll=None): + setup_settings_device(click, pm) + scroll(-400, 278, 962) + click(278, 646) + time.sleep(UI_DELAY) + def setup_settings_vehicle(click, pm: PubMaster, scroll=None): Params().put("CarPlatformBundle", json.dumps( { @@ -291,6 +297,7 @@ CASES.update({ "settings_steering": setup_settings_steering, "settings_steering_mads": setup_settings_steering_mads, "settings_steering_alc": setup_settings_steering_alc, + "settings_driving": setup_settings_driving, "settings_trips": setup_settings_trips, "settings_vehicle": setup_settings_vehicle, }) From db6832762b054f47627ab66165cdc8f05857d1a4 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 2 May 2025 21:48:55 -0700 Subject: [PATCH 103/477] Tesla: forward Summon (#35113) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 6af5ad8dd5..b82e8e0122 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 6af5ad8dd5de3b890d2812cc19b063caae858b31 +Subproject commit b82e8e01225a2b9e0953f59d336b9d278bab9acf From 44ab494c4105660b272a79fbce5d853235b2d30d Mon Sep 17 00:00:00 2001 From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Date: Fri, 2 May 2025 23:50:40 -0700 Subject: [PATCH 104/477] BUG FIX: Fix .thneed model runners (#871) Add support for thneed library and thneedmodel integration Co-authored-by: DevTekVE --- sunnypilot/modeld/SConscript | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sunnypilot/modeld/SConscript b/sunnypilot/modeld/SConscript index bebd2de953..df897e3049 100644 --- a/sunnypilot/modeld/SConscript +++ b/sunnypilot/modeld/SConscript @@ -52,3 +52,8 @@ commonmodel_lib = lenv.Library('commonmodel', common_src) lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks) lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath) lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks) + +if arch == "larch64": + thneed_lib = env.SharedLibrary('thneed', thneed_src, LIBS=[gpucommon, common, 'OpenCL', 'dl']) + thneedmodel_lib = env.Library('thneedmodel', ['runners/thneedmodel.cc']) + lenvCython.Program('runners/thneedmodel_pyx.so', 'runners/thneedmodel_pyx.pyx', LIBS=envCython["LIBS"]+[thneedmodel_lib, thneed_lib, gpucommon, common, 'dl', 'OpenCL']) \ No newline at end of file From 600647d5e2434f8ed77cafe08cdbb8d34645c8a8 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 3 May 2025 21:01:17 +0200 Subject: [PATCH 105/477] models: simplify modeld v2 logic process (#875) * Refactor model runner methods for improved abstraction. Moved slicing logic to a private `_slice_outputs` method and decoupled `_run_model` for clearer subclass implementation. Removed redundant `output` attribute in `ModelState` to streamline data handling. * Add output parsing to model_runner and remove duplicate logic Integrates an output parser directly into `model_runner` for streamlined inference and parsing. Removes redundant parser initialization from `modeld` to avoid duplication and enhance maintainability. * Reordering * linter * linter --- sunnypilot/modeld_v2/model_runner.py | 16 ++++++++++++---- sunnypilot/modeld_v2/modeld.py | 10 +--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sunnypilot/modeld_v2/model_runner.py b/sunnypilot/modeld_v2/model_runner.py index 82fc3f5196..64bf52795d 100644 --- a/sunnypilot/modeld_v2/model_runner.py +++ b/sunnypilot/modeld_v2/model_runner.py @@ -8,6 +8,7 @@ from openpilot.sunnypilot.modeld_v2 import MODEL_PATH, MODEL_PKL_PATH, METADATA_ from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLMem from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address +from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser from openpilot.system.hardware import TICI from openpilot.system.hardware.hw import Paths @@ -48,6 +49,7 @@ class ModelRunner(ABC): self.input_shapes = self.model_metadata['input_shapes'] self.output_slices = self.model_metadata['output_slices'] self.inputs: dict = {} + self.parser = Parser() @abstractmethod def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict: @@ -55,16 +57,22 @@ class ModelRunner(ABC): raise NotImplementedError @abstractmethod - def run_model(self): + def _run_model(self): """Run model inference with prepared inputs.""" + raise NotImplementedError("This method should be implemented in subclasses.") - def slice_outputs(self, model_outputs: np.ndarray) -> dict: + def _slice_outputs(self, model_outputs: np.ndarray) -> dict: """Slice model outputs according to metadata configuration.""" parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()} if SEND_RAW_PRED: parsed_outputs['raw_pred'] = model_outputs.copy() return parsed_outputs + def run_model(self) -> dict[str, np.ndarray]: + """Run model inference with prepared inputs and parse outputs.""" + result: dict[str, np.ndarray] = self.parser.parse_outputs(self._slice_outputs(self._run_model())) + return result + class TinygradRunner(ModelRunner): """Tinygrad implementation of model runner for TICI hardware.""" @@ -108,7 +116,7 @@ class TinygradRunner(ModelRunner): return self.inputs - def run_model(self): + def _run_model(self): return self.model_run(**self.inputs).numpy().flatten() @@ -130,5 +138,5 @@ class ONNXRunner(ModelRunner): self.inputs[key] = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=self.input_to_nptype[key]) return self.inputs - def run_model(self): + def _run_model(self): return self.runner.run(None, self.inputs)[0].flatten() diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index bc67db297d..9a90e21cb5 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -18,7 +18,6 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser from openpilot.sunnypilot.modeld_v2.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.sunnypilot.modeld_v2.constants import ModelConstants from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext @@ -41,7 +40,6 @@ class FrameMeta: class ModelState: frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] - output: np.ndarray prev_desire: np.ndarray # for tracking the rising edge of the pulse def __init__(self, context: CLContext): @@ -64,12 +62,7 @@ class ModelState: if key not in self.frames: # Managed by opencl self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32) - self.parser = Parser() - if self.model_runner.is_20hz: - net_output_size = self.model_runner.model_metadata['output_shapes']['outputs'][1] - self.output = np.zeros(net_output_size, dtype=np.float32) - num_elements = self.numpy_inputs['features_buffer'].shape[1] step_size = int(-100 / num_elements) self.full_features_20Hz_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] @@ -105,8 +98,7 @@ class ModelState: return None # Run model inference - self.output = self.model_runner.run_model() - outputs = self.parser.parse_outputs(self.model_runner.slice_outputs(self.output)) + outputs = self.model_runner.run_model() if self.model_runner.is_20hz: self.full_features_20Hz[:-1] = self.full_features_20Hz[1:] From d90e41f08f8637b2781d5449d98b475601bda39f Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 4 May 2025 17:03:22 +0200 Subject: [PATCH 106/477] models: refactor model bundle structure (#870) * Refactor model and artifact structures with version compatibility filtering - Introduced `Artifact` struct and nested it within the `Model` struct for improved clarity and organization. - Updated enums, logic, and parsing to align with the new struct definitions. - Implemented version compatibility filtering for model bundles using the `is_bundle_version_compatible` helper. - Enhanced artifact download handling by adding checks for missing URIs, better error management, and improved logging. - Adjusted model fetching to point to the latest endpoint (`v3`). * Make linter happy * Make linter happy * Refactor model data parsing to improve readability. Replaced kwargs-based data extraction with explicit parameter passing for clarity. This enhances code readability and reduces ambiguities in method calls, making the parsing logic more maintainable and straightforward. * Refactor error handling in active model bundle retrieval. Wrapped the logic to fetch the active model bundle in a try-except block to prevent unhandled exceptions. This ensures more robust error handling and avoids potential crashes when retrieving or processing model data. * Refactor exception handling in get_active_model_bundle Replace bare except with Exception to improve specificity and clarity. This ensures better debugging practices and aligns with recommended coding standards. Other minor whitespace adjustments were made for improved readability. * Update model path to use artifact fileName property Replaced `fileName` with `artifact.fileName` in the custom model path construction. This ensures compatibility with updated drive model structures and avoids potential file resolution issues. --- cereal/custom.capnp | 35 ++++--- .../qt/offroad/settings/software_panel.cc | 18 ++-- sunnypilot/modeld/runners/run_helpers.py | 17 ++-- sunnypilot/modeld_v2/model_runner.py | 6 +- sunnypilot/models/fetcher.py | 95 +++++++++---------- sunnypilot/models/helpers.py | 33 ++++++- sunnypilot/models/manager.py | 42 +++++--- 7 files changed, 146 insertions(+), 100 deletions(-) diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 8c23743420..10fc83eb01 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -39,20 +39,6 @@ struct ModelManagerSP @0xaedffd8f31e7b55d { sha256 @1 :Text; } - enum Type { - drive @0; - navigation @1; - metadata @2; - } - - struct Model { - fullName @0 :Text; - fileName @1 :Text; - downloadUri @2 :DownloadUri; - downloadProgress @3 :DownloadProgress; - type @4 :Type; - } - enum DownloadStatus { notDownloading @0; downloading @1; @@ -67,6 +53,25 @@ struct ModelManagerSP @0xaedffd8f31e7b55d { eta @2 :UInt32; } + struct Artifact { + fileName @0 :Text; + downloadUri @1 :DownloadUri; + downloadProgress @2 :DownloadProgress; + } + + struct Model { + type @0 :Type; + artifact @1 :Artifact; # Main artifact + metadata @2 :Artifact; # Metadata artifact + + enum Type { + supercombo @0; + navigation @1; + vision @2; + policy @3; + } + } + enum Runner { snpe @0; tinygrad @1; @@ -83,6 +88,8 @@ struct ModelManagerSP @0xaedffd8f31e7b55d { environment @6 :Text; runner @7 :Runner; is20hz @8 :Bool; + ref @9 :Text; # New field + minimumSelectorVersion @10 :UInt32; } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc index ddee1362bb..20f3fdb598 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc @@ -47,24 +47,24 @@ void SoftwarePanelSP::handleBundleDownloadProgress() { // Get status for each model type in order for (const auto &model: models) { QString typeName; - QString modelName; + QString modelName = QString::fromStdString(bundle.getDisplayName()); switch (model.getType()) { - case cereal::ModelManagerSP::Type::DRIVE: + case cereal::ModelManagerSP::Model::Type::SUPERCOMBO: typeName = tr("Driving"); - modelName = QString::fromStdString(bundle.getDisplayName()); break; - case cereal::ModelManagerSP::Type::NAVIGATION: + case cereal::ModelManagerSP::Model::Type::NAVIGATION: typeName = tr("Navigation"); - modelName = QString::fromStdString(model.getFullName()); break; - case cereal::ModelManagerSP::Type::METADATA: - typeName = tr("Metadata"); - modelName = QString::fromStdString(model.getFullName()); + case cereal::ModelManagerSP::Model::Type::VISION: + typeName = tr("Vision"); + break; + case cereal::ModelManagerSP::Model::Type::POLICY: + typeName = tr("Policy"); break; } - const auto &progress = model.getDownloadProgress(); + const auto &progress = model.getArtifact().getDownloadProgress(); QString line; if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING) { diff --git a/sunnypilot/modeld/runners/run_helpers.py b/sunnypilot/modeld/runners/run_helpers.py index 5470106f1d..3e6b280ae1 100644 --- a/sunnypilot/modeld/runners/run_helpers.py +++ b/sunnypilot/modeld/runners/run_helpers.py @@ -25,13 +25,19 @@ METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl' ModelManager = custom.ModelManagerSP +def _get_model(): + if bundle := get_active_bundle(): + drive_model = next(model for model in bundle.models if model.type == ModelManager.Model.Type.supercombo) + return drive_model + + return None + def get_model_path(): if USE_ONNX: return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'} - if bundle := get_active_bundle(): - drive_model = next(model for model in bundle.models if model.type == ModelManager.Type.drive) - return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{drive_model.fileName}"} + if model := _get_model(): + return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{model.fileName}"} return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'} @@ -39,9 +45,8 @@ def get_model_path(): def load_metadata(): metadata_path = METADATA_PATH - if bundle := get_active_bundle(): - metadata_model = next(model for model in bundle.models if model.type == ModelManager.Type.metadata) - metadata_path = f"{CUSTOM_MODEL_PATH}/{metadata_model.fileName}" + if model := _get_model(): + metadata_path = f"{CUSTOM_MODEL_PATH}/{model.metadata.fileName}" with open(metadata_path, 'rb') as f: return pickle.load(f) diff --git a/sunnypilot/modeld_v2/model_runner.py b/sunnypilot/modeld_v2/model_runner.py index 64bf52795d..10c80b8769 100644 --- a/sunnypilot/modeld_v2/model_runner.py +++ b/sunnypilot/modeld_v2/model_runner.py @@ -35,8 +35,8 @@ class ModelRunner(ABC): if bundle := get_active_bundle(): bundle_models = {model.type.raw: model for model in bundle.models} - self._drive_model = bundle_models.get(ModelManager.Type.drive) - self._metadata_model = bundle_models.get(ModelManager.Type.metadata) + self._drive_model = bundle_models.get(ModelManager.Model.Type.supercombo) + self._metadata_model = self._drive_model.metadata self.is_20hz = bundle.is20hz # Override the metadata path if a metadata model is found in the active bundle @@ -82,7 +82,7 @@ class TinygradRunner(ModelRunner): model_pkl_path = MODEL_PKL_PATH if self._drive_model: - model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.fileName}" + model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.artifact.fileName}" assert model_pkl_path.endswith('_tinygrad.pkl'), f"Invalid model file: {model_pkl_path} for TinygradRunner" # Load Tinygrad model diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py index 3e3e479903..7076cf1861 100644 --- a/sunnypilot/models/fetcher.py +++ b/sunnypilot/models/fetcher.py @@ -11,6 +11,7 @@ import time import requests from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog +from sunnypilot.models.helpers import is_bundle_version_compatible from cereal import custom @@ -19,68 +20,49 @@ class ModelParser: """Handles parsing of model data into cereal objects""" @staticmethod - def _parse_model(full_name: str, file_name: str, uri_data: dict, - model_type: custom.ModelManagerSP.Type) -> custom.ModelManagerSP.Model: - model = custom.ModelManagerSP.Model() + def _parse_download_uri(download_uri_data) -> custom.ModelManagerSP.DownloadUri: download_uri = custom.ModelManagerSP.DownloadUri() + download_uri.uri = download_uri_data.get("url") + download_uri.sha256 = download_uri_data.get("sha256") + return download_uri - download_uri.uri = uri_data["url"] - download_uri.sha256 = uri_data["sha256"] + @staticmethod + def _parse_artifact(artifact_data) -> custom.ModelManagerSP.Artifact: + artifact = custom.ModelManagerSP.Artifact() + artifact.fileName = artifact_data.get("file_name") + artifact.downloadUri = ModelParser._parse_download_uri(artifact_data.get("download_uri", {})) + return artifact - model.fullName = full_name - model.fileName = file_name - model.downloadUri = download_uri - model.type = model_type + @staticmethod + def _parse_model(model_data) -> custom.ModelManagerSP.Model: + model = custom.ModelManagerSP.Model() + model.type = model_data.get("type") + model.artifact = ModelParser._parse_artifact(model_data.get("artifact", {})) + if metadata := model_data.get("metadata"): + model.metadata = ModelParser._parse_artifact(metadata) return model @staticmethod - def _parse_bundle(key: str, value: dict) -> custom.ModelManagerSP.ModelBundle: + def _parse_bundle(bundle) -> custom.ModelManagerSP.ModelBundle: model_bundle = custom.ModelManagerSP.ModelBundle() - - # Parse main driving model - models = [ - ModelParser._parse_model( - value["full_name"], - value["file_name"], - value["download_uri"], - custom.ModelManagerSP.Type.drive - ) - ] - - # Parse navigation model if exists - if value.get("download_uri_nav"): - models.append(ModelParser._parse_model( - value["full_name_nav"], - value["file_name_nav"], - value["download_uri_nav"], - custom.ModelManagerSP.Type.navigation - )) - - # Parse metadata model if exists - if value.get("download_uri_metadata"): - models.append(ModelParser._parse_model( - value["full_name_metadata"], - value["file_name_metadata"], - value["download_uri_metadata"], - custom.ModelManagerSP.Type.metadata - )) - - model_bundle.index = int(value["index"]) - model_bundle.internalName = key - model_bundle.displayName = value["display_name"] - model_bundle.models = models + model_bundle.index = int(bundle["index"]) + model_bundle.internalName = bundle["short_name"] + model_bundle.displayName = bundle["display_name"] + model_bundle.models = [ModelParser._parse_model(model) for model in bundle.get("models",[])] model_bundle.status = 0 - model_bundle.generation = int(value["generation"]) - model_bundle.environment = value["environment"] - model_bundle.runner = value.get("runner", custom.ModelManagerSP.Runner.snpe) - model_bundle.is20hz = value.get("is_20hz", False) + model_bundle.generation = int(bundle["generation"]) + model_bundle.environment = bundle["environment"] + model_bundle.runner = bundle.get("runner", custom.ModelManagerSP.Runner.snpe) + model_bundle.is20hz = bundle.get("is_20hz", False) + model_bundle.minimumSelectorVersion = int(bundle["minimum_selector_version"]) return model_bundle @staticmethod def parse_models(json_data: dict) -> list[custom.ModelManagerSP.ModelBundle]: - return [ModelParser._parse_bundle(key, value) for key, value in json_data.items()] + found_bundles = [ModelParser._parse_bundle(bundle) for bundle in json_data.get("bundles", [])] + return [bundle for bundle in found_bundles if is_bundle_version_compatible(bundle.to_dict())] class ModelCache: @@ -122,7 +104,7 @@ class ModelCache: class ModelFetcher: """Handles fetching and caching of model data from remote source""" - MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v2.json" + MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v3.json" def __init__(self, params: Params): self.params = params @@ -143,7 +125,7 @@ class ModelFetcher: cloudlog.exception("Error fetching models") raise - def get_available_models(self) -> list[custom.ModelManagerSP.ModelBundle]: + def get_available_bundles(self) -> list[custom.ModelManagerSP.ModelBundle]: """Gets the list of available models, with smart cache handling""" cached_data, is_expired = self.model_cache.get() @@ -160,3 +142,16 @@ class ModelFetcher: cloudlog.warning("Failed to fetch fresh data. Using expired cache as fallback") return self.model_parser.parse_models(cached_data) + +if __name__ == "__main__": + params = Params() + model_fetcher = ModelFetcher(params) + bundles = model_fetcher.get_available_bundles() + for bundle in bundles: + for model in bundle.models: + # Print model details + print(f"Bundle: {bundle.internalName}, Type: {model.type}, Status: {bundle.status}") + # Print artifact details + print(f"Artifact: {model.artifact.fileName}, Download URI: {model.artifact.downloadUri.uri}") + # Print metadata details + print(f"Metadata: {model.metadata.fileName}, Download URI: {model.metadata.downloadUri.uri}") diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py index dbdef55900..20f58f8c59 100644 --- a/sunnypilot/models/helpers.py +++ b/sunnypilot/models/helpers.py @@ -8,7 +8,11 @@ See the LICENSE.md file in the root directory for more details. import hashlib import os from openpilot.common.params import Params -from cereal import custom, messaging +from cereal import custom +import json + +CURRENT_SELECTOR_VERSION = 2 +REQUIRED_MIN_SELECTOR_VERSION = 2 async def verify_file(file_path: str, expected_hash: str) -> bool: @@ -24,13 +28,36 @@ async def verify_file(file_path: str, expected_hash: str) -> bool: return sha256_hash.hexdigest().lower() == expected_hash.lower() +def is_bundle_version_compatible(bundle: dict) -> bool: + """ + Checks whether the model bundle is compatible with the current selector version constraints. + + The bundle specifies a `minimum_selector_version`, which defines the minimum selector version + required to load the model. This function ensures that: + + 1. The model is not too old: the bundle must require at least `REQUIRED_MIN_SELECTOR_VERSION`. + 2. The model is not too new: it must support the current selector version (`CURRENT_SELECTOR_VERSION`). + + This allows the selector to enforce both a minimum and maximum range of supported models, + even if a model would otherwise be compatible. + + :param bundle: Dictionary containing `minimum_selector_version`, as defined by the model bundle. + :type bundle: Dict + :return: True if the selector version is within the accepted range for the bundle; otherwise False. + :rtype: Bool + """ + return bool(REQUIRED_MIN_SELECTOR_VERSION <= bundle.get("minimumSelectorVersion", 0) <= CURRENT_SELECTOR_VERSION) + def get_active_bundle(params: Params = None) -> custom.ModelManagerSP.ModelBundle: """Gets the active model bundle from cache""" if params is None: params = Params() - if active_bundle := params.get("ModelManager_ActiveBundle"): - return messaging.log_from_bytes(active_bundle, custom.ModelManagerSP.ModelBundle) + try: + if (active_bundle := json.loads(params.get("ModelManager_ActiveBundle") or "{}")) and is_bundle_version_compatible(active_bundle): + return custom.ModelManagerSP.ModelBundle(**active_bundle) + except Exception: + pass return None diff --git a/sunnypilot/models/manager.py b/sunnypilot/models/manager.py index 5fbad6cd8e..630d6a8d77 100644 --- a/sunnypilot/models/manager.py +++ b/sunnypilot/models/manager.py @@ -8,6 +8,7 @@ See the LICENSE.md file in the root directory for more details. import asyncio import os import time +import json import aiohttp from openpilot.common.params import Params @@ -73,43 +74,54 @@ class ModelManagerSP: # Clean up start time after download completes del self._download_start_times[model.fileName] - async def _process_model(self, model, destination_path: str) -> None: + async def _process_artifact(self, artifact, destination_path: str) -> None: """Processes a single model download including verification""" - url = model.downloadUri.uri - expected_hash = model.downloadUri.sha256 - filename = model.fileName + if not artifact.downloadUri.uri: + return None + + url = artifact.downloadUri.uri + expected_hash = artifact.downloadUri.sha256 + filename = artifact.fileName full_path = os.path.join(destination_path, filename) try: # Check existing file if os.path.exists(full_path) and await verify_file(full_path, expected_hash): - model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached - model.downloadProgress.progress = 100 - model.downloadProgress.eta = 0 + artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached + artifact.downloadProgress.progress = 100 + artifact.downloadProgress.eta = 0 self._report_status() return # Download and verify - await self._download_file(url, full_path, model) + await self._download_file(url, full_path, artifact) if not await verify_file(full_path, expected_hash): raise ValueError(f"Hash validation failed for {filename}") - model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded - model.downloadProgress.eta = 0 + artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded + artifact.downloadProgress.eta = 0 self._report_status() except Exception as e: cloudlog.error(f"Error downloading {filename}: {str(e)}") if os.path.exists(full_path): os.remove(full_path) - model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed - model.downloadProgress.eta = 0 + artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed + artifact.downloadProgress.eta = 0 self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.failed self._report_status() # Clean up start time if it exists - self._download_start_times.pop(model.fileName, None) + self._download_start_times.pop(artifact.fileName, None) raise + async def _process_model(self, model, destination_path: str) -> None: + """Processes a single model download including verification""" + model_artifact = model.artifact + metadata_artifact = model.metadata + + await self._process_artifact(metadata_artifact, destination_path) + await self._process_artifact(model_artifact, destination_path) + def _report_status(self) -> None: """Reports current status through messaging system""" msg = messaging.new_message('modelManagerSP', valid=True) @@ -134,7 +146,7 @@ class ModelManagerSP: await asyncio.gather(*tasks) self.active_bundle = self.selected_bundle self.active_bundle.status = custom.ModelManagerSP.DownloadStatus.downloaded - self.params.put("ModelManager_ActiveBundle", self.active_bundle.to_bytes()) + self.params.put("ModelManager_ActiveBundle", json.dumps(self.active_bundle.to_dict())) self.selected_bundle = None except Exception: @@ -154,7 +166,7 @@ class ModelManagerSP: while True: try: - self.available_models = self.model_fetcher.get_available_models() + self.available_models = self.model_fetcher.get_available_bundles() self.active_bundle = get_active_bundle(self.params) if index_to_download := self.params.get("ModelManager_DownloadIndex", block=False, encoding="utf-8"): From 8c8b2c448801cdcfb4aa6513a0211390cc7aa012 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 5 May 2025 01:15:25 +0800 Subject: [PATCH 107/477] replay: fix potential timestamp parsing error in Route::load (#35117) Fix potential timestamp parsing error in Route::load --- tools/replay/route.cc | 6 ++++-- tools/replay/route.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/replay/route.cc b/tools/replay/route.cc index ff5a858e8c..98fa0e290e 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -50,9 +50,11 @@ bool Route::load() { return false; } + // Parse the timestamp from the route identifier (only applicable for old route formats). struct tm tm_time = {0}; - strptime(route_.timestamp.c_str(), "%Y-%m-%d--%H-%M-%S", &tm_time); - date_time_ = mktime(&tm_time); + if (strptime(route_.timestamp.c_str(), "%Y-%m-%d--%H-%M-%S", &tm_time)) { + date_time_ = mktime(&tm_time); + } bool ret = data_dir_.empty() ? loadFromServer() : loadFromLocal(); if (ret) { diff --git a/tools/replay/route.h b/tools/replay/route.h index 1806be5afa..fb9f1869f6 100644 --- a/tools/replay/route.h +++ b/tools/replay/route.h @@ -59,7 +59,7 @@ protected: RouteIdentifier route_ = {}; std::string data_dir_; std::map segments_; - std::time_t date_time_; + std::time_t date_time_ = 0; RouteLoadError err_ = RouteLoadError::None; }; From 8ee99523f4e9070a15b9edce6bc4fd5dde8c70aa Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 5 May 2025 01:15:47 +0800 Subject: [PATCH 108/477] cleanup .gitignore (#35116) --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 834b463083..40438f5fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -47,10 +47,8 @@ selfdrive/pandad/pandad cereal/services.h cereal/gen cereal/messaging/bridge -selfdrive/logcatd/logcatd selfdrive/mapd/default_speeds_by_region.json system/proclogd/proclogd -selfdrive/ui/translations/alerts_generated.h selfdrive/ui/translations/tmp selfdrive/test/longitudinal_maneuvers/out selfdrive/car/tests/cars_dump From f704d18a8b5c7a4428d3e28828d1eb1ac4cf04ee Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Sun, 4 May 2025 10:16:35 -0700 Subject: [PATCH 109/477] feat(clip): title and metadata overlay (#35099) * wip * moar * ensure inter is installed * line len * refactor * dont need this * no longer than * show meta for 4s --- tools/clip/run.py | 78 ++++++++++++++++++++-------- tools/install_ubuntu_dependencies.sh | 1 + tools/lib/route.py | 7 +++ 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 97baa880ea..2384edc4bb 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -16,7 +16,7 @@ from typing import Literal from cereal.messaging import SubMaster from openpilot.common.basedir import BASEDIR from openpilot.common.prefix import OpenpilotPrefix -from openpilot.tools.lib.route import SegmentRange, get_max_seg_number_cached +from openpilot.tools.lib.route import Route DEFAULT_OUTPUT = 'output.mp4' DEMO_START = 90 @@ -26,7 +26,7 @@ FRAMERATE = 20 PIXEL_DEPTH = '24' RESOLUTION = '2160x1080' SECONDS_TO_WARM = 2 -PROC_WAIT_SECONDS = 5 +PROC_WAIT_SECONDS = 30 REPLAY = str(Path(BASEDIR, 'tools/replay/replay').resolve()) UI = str(Path(BASEDIR, 'selfdrive/ui/ui').resolve()) @@ -52,6 +52,29 @@ def check_for_failure(proc: Popen): raise ChildProcessError(msg) +def escape_ffmpeg_text(value: str): + special_chars = {',': '\\,', ':': '\\:', '=': '\\=', '[': '\\[', ']': '\\]'} + value = value.replace('\\', '\\\\\\\\\\\\\\\\') + for char, escaped in special_chars.items(): + value = value.replace(char, escaped) + return value + + +def get_meta_text(route: Route): + metadata = route.get_metadata() + origin_parts = metadata['git_remote'].split('/') + origin = origin_parts[3] if len(origin_parts) > 3 else 'unknown' + return ', '.join([ + f'openpilot v{metadata['version']}', + f'route: {metadata['fullname']}', + f'car: {metadata['platform']}', + f'origin: {origin}', + f'branch: {metadata['git_branch']}', + f'commit: {metadata['git_commit'][:7]}', + f'modified: {str(metadata['git_dirty']).lower()}', + ]) + + def parse_args(parser: ArgumentParser): args = parser.parse_args() if args.demo: @@ -74,20 +97,17 @@ def parse_args(parser: ArgumentParser): if args.start < SECONDS_TO_WARM: parser.error(f'start must be greater than {SECONDS_TO_WARM}s to allow the UI time to warm up') - # if using local files, don't worry about length check right now so we skip the network call - # TODO: derive segment count from local FS - if not args.data_dir: - try: - num_segs = get_max_seg_number_cached(SegmentRange(args.route)) - except Exception as e: - parser.error(f'failed to get route length: {e}') + try: + args.route = Route(args.route, data_dir=args.data_dir) + except Exception as e: + parser.error(f'failed to get route: {e}') - # FIXME: length isn't exactly max segment seconds, simplify to replay exiting at end of data - length = round(num_segs * 60) - if args.start >= length: - parser.error(f'start ({args.start}s) cannot be after end of route ({length}s)') - if args.end > length: - parser.error(f'end ({args.end}s) cannot be after end of route ({length}s)') + # FIXME: length isn't exactly max segment seconds, simplify to replay exiting at end of data + length = round(args.route.max_seg_number * 60) + if args.start >= length: + parser.error(f'start ({args.start}s) cannot be after end of route ({length}s)') + if args.end > length: + parser.error(f'end ({args.end}s) cannot be after end of route ({length}s)') return args @@ -119,6 +139,12 @@ def validate_route(route: str): return route +def validate_title(title: str): + if len(title) > 80: + raise ArgumentTypeError('title must be no longer than 80 chars') + return title + + def wait_for_frames(procs: list[Popen]): sm = SubMaster(['uiDebug']) no_frames_drawn = True @@ -129,20 +155,27 @@ def wait_for_frames(procs: list[Popen]): check_for_failure(proc) -def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, route: str, output_filepath: str, start: int, end: int, target_size_mb: int): - logger.info(f'clipping route {route}, start={start} end={end} quality={quality} target_filesize={target_size_mb}MB') +def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, route: Route, out: str, start: int, end: int, target_mb: int, title: str | None): + logger.info(f'clipping route {route.name.canonical_name}, start={start} end={end} quality={quality} target_filesize={target_mb}MB') begin_at = max(start - SECONDS_TO_WARM, 0) duration = end - start - bit_rate_kbps = int(round(target_size_mb * 8 * 1024 * 1024 / duration / 1000)) + bit_rate_kbps = int(round(target_mb * 8 * 1024 * 1024 / duration / 1000)) # TODO: evaluate creating fn that inspects /tmp/.X11-unix and creates unused display to avoid possibility of collision display = f':{randint(99, 999)}' + meta_text = get_meta_text(route) + overlays = [ + f"drawtext=text='{escape_ffmpeg_text(meta_text)}':fontfile=Inter.tff:fontcolor=white:fontsize=18:box=1:boxcolor=black@0.33:boxborderw=7:x=(w-text_w)/2:y=5.5:enable='between(t,1,5)'" + ] + if title: + overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile=Inter.tff:fontcolor=white:fontsize=32:box=1:boxcolor=black@0.33:boxborderw=10:x=(w-text_w)/2:y=53") + ffmpeg_cmd = [ 'ffmpeg', '-y', '-video_size', RESOLUTION, '-framerate', str(FRAMERATE), '-f', 'x11grab', '-draw_mouse', '0', '-i', display, '-c:v', 'libx264', '-maxrate', f'{bit_rate_kbps}k', '-bufsize', f'{bit_rate_kbps*2}k', '-crf', '23', - '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', '-f', 'mp4', '-t', str(duration), output_filepath, + '-filter:v', ','.join(overlays), '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', '-f', 'mp4', '-t', str(duration), out ] replay_cmd = [REPLAY, '-c', '1', '-s', str(begin_at), '--prefix', prefix] @@ -150,7 +183,7 @@ def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, rou replay_cmd.extend(['--data_dir', data_dir]) if quality == 'low': replay_cmd.append('--qcam') - replay_cmd.append(route) + replay_cmd.append(route.name.canonical_name) ui_cmd = [UI, '-platform', 'xcb'] xvfb_cmd = ['Xvfb', display, '-terminate', '-screen', '0', f'{RESOLUTION}x{PIXEL_DEPTH}'] @@ -183,7 +216,7 @@ def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, rou ffmpeg_proc.wait(duration + PROC_WAIT_SECONDS) for proc in procs: check_for_failure(proc) - logger.info(f'recording complete: {Path(output_filepath).resolve()}') + logger.info(f'recording complete: {Path(out).resolve()}') def main(): @@ -199,9 +232,10 @@ def main(): p.add_argument('-p', '--prefix', help='openpilot prefix', default=f'clip_{randint(100, 99999)}') p.add_argument('-q', '--quality', help='quality of camera (low = qcam, high = hevc)', choices=['low', 'high'], default='high') p.add_argument('-s', '--start', help='start clipping at seconds', type=int) + p.add_argument('-t', '--title', help='overlay this title on the video (e.g. "Chill driving across the Golden Gate Bridge")', type=validate_title) args = parse_args(p) try: - clip(args.data_dir, args.quality, args.prefix, args.route, args.output, args.start, args.end, args.file_size) + clip(args.data_dir, args.quality, args.prefix, args.route, args.output, args.start, args.end, args.file_size, args.title) except KeyboardInterrupt as e: logger.exception('interrupted by user', exc_info=e) except Exception as e: diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index f33569704a..3d6fd2e7a9 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -33,6 +33,7 @@ function install_ubuntu_common_requirements() { git \ git-lfs \ ffmpeg \ + fonts-inter \ libavformat-dev \ libavcodec-dev \ libavdevice-dev \ diff --git a/tools/lib/route.py b/tools/lib/route.py index ddb8b0486e..831a1e2ee5 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -21,6 +21,7 @@ class Route: def __init__(self, name, data_dir=None): self._name = RouteName(name) self.files = None + self.metadata = None if data_dir is not None: self._segments = self._get_segments_local(data_dir) else: @@ -59,6 +60,12 @@ class Route: qcamera_path_by_seg_num = {s.name.segment_num: s.qcamera_path for s in self._segments} return [qcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number + 1)] + def get_metadata(self): + if not self.metadata: + api = CommaApi(get_token()) + self.metadata = api.get('v1/route/' + self.name.canonical_name) + return self.metadata + # TODO: refactor this, it's super repetitive def _get_segments_remote(self): api = CommaApi(get_token()) From 87fae0c6f23948f58336947c23c593860076677b Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Sun, 4 May 2025 10:16:48 -0700 Subject: [PATCH 110/477] fix(clip): longer timeout for ffmpeg to finish (#35092) From c33e5b3209b0c4f925b41f3c99f3a9867d8ba072 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 5 May 2025 23:28:12 +0800 Subject: [PATCH 111/477] python ui: wifi manager (#34814) * python wifi manager * fix ui * need auth callback * move to widgets * confirm forgot * add drag detection * improve keyboard & list * remove duplicate * typos * use gui_app render * refactor * cleanup * cleanup * shutdown * fix types * revert * scroll panel cleanup * reset is_dragging on mouse release * Revert "reset is_dragging on mouse release" This reverts commit ff5e51cf6f00848d93aa3ce0bab16602fea7a319. * unformat * cleanup * update state when connecting * forgotten callback * maybe fix? first tap didn't work * Revert "maybe fix? first tap didn't work" This reverts commit 739f0e3bd37323d10479b081a20d05c7fdff5495. * remove set_target_fps * Revert "remove set_target_fps" This reverts commit 96f74553ef6fa2ab2a4d1a07880175e7da628c9f. --------- Co-authored-by: Cameron Clough --- system/ui/lib/scroll_panel.py | 17 +- system/ui/lib/wifi_manager.py | 481 ++++++++++++++++++++++++++++++++++ system/ui/widgets/keyboard.py | 34 ++- system/ui/widgets/network.py | 172 ++++++++++++ 4 files changed, 692 insertions(+), 12 deletions(-) create mode 100644 system/ui/lib/wifi_manager.py create mode 100644 system/ui/widgets/network.py diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index 0dc757ff6f..7dc2fdb917 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -4,6 +4,7 @@ from enum import IntEnum MOUSE_WHEEL_SCROLL_SPEED = 30 INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia +DRAG_THRESHOLD = 5 # Pixels of movement to consider it a drag, not a click class ScrollState(IntEnum): @@ -16,10 +17,12 @@ class GuiScrollPanel: def __init__(self, show_vertical_scroll_bar: bool = False): self._scroll_state: ScrollState = ScrollState.IDLE self._last_mouse_y: float = 0.0 + self._start_mouse_y: float = 0.0 # Track the initial mouse position for drag detection self._offset = rl.Vector2(0, 0) self._view = rl.Rectangle(0, 0, 0, 0) self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar self._velocity_y = 0.0 # Velocity for inertia + self._is_dragging = False def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2: mouse_pos = rl.get_mouse_position() @@ -35,20 +38,27 @@ class GuiScrollPanel: self._scroll_state = ScrollState.DRAGGING_SCROLLBAR self._last_mouse_y = mouse_pos.y + self._start_mouse_y = mouse_pos.y # Record starting position self._velocity_y = 0.0 # Reset velocity when drag starts + self._is_dragging = False # Reset dragging flag if self._scroll_state != ScrollState.IDLE: if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): delta_y = mouse_pos.y - self._last_mouse_y + # Check if movement exceeds the drag threshold + total_drag = abs(mouse_pos.y - self._start_mouse_y) + if total_drag > DRAG_THRESHOLD: + self._is_dragging = True + if self._scroll_state == ScrollState.DRAGGING_CONTENT: self._offset.y += delta_y - else: + elif self._scroll_state == ScrollState.DRAGGING_SCROLLBAR: delta_y = -delta_y self._last_mouse_y = mouse_pos.y self._velocity_y = delta_y # Update velocity during drag - else: + elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): self._scroll_state = ScrollState.IDLE # Handle mouse wheel scrolling @@ -73,3 +83,6 @@ class GuiScrollPanel: self._offset.y = max(min(self._offset.y, 0), -max_scroll_y) return self._offset + + def is_click_valid(self) -> bool: + return self._scroll_state == ScrollState.IDLE and not self._is_dragging and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py new file mode 100644 index 0000000000..af533e9a2a --- /dev/null +++ b/system/ui/lib/wifi_manager.py @@ -0,0 +1,481 @@ +import asyncio +import threading +import time +import uuid +from collections.abc import Callable +from dataclasses import dataclass +from enum import IntEnum + +from dbus_next.aio import MessageBus +from dbus_next import BusType, Variant, Message +from dbus_next.errors import DBusError +from dbus_next.constants import MessageType +from openpilot.common.swaglog import cloudlog + +# NetworkManager constants +NM = "org.freedesktop.NetworkManager" +NM_PATH = '/org/freedesktop/NetworkManager' +NM_IFACE = 'org.freedesktop.NetworkManager' +NM_SETTINGS_PATH = '/org/freedesktop/NetworkManager/Settings' +NM_SETTINGS_IFACE = 'org.freedesktop.NetworkManager.Settings' +NM_CONNECTION_IFACE = 'org.freedesktop.NetworkManager.Settings.Connection' +NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless' +NM_PROPERTIES_IFACE = 'org.freedesktop.DBus.Properties' +NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device" + +NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8 + +# NetworkManager device states +class NMDeviceState(IntEnum): + DISCONNECTED = 30 + PREPARE = 40 + NEED_AUTH = 60 + IP_CONFIG = 70 + ACTIVATED = 100 + +class SecurityType(IntEnum): + OPEN = 0 + WPA = 1 + WPA2 = 2 + WPA3 = 3 + UNSUPPORTED = 4 + +@dataclass +class NetworkInfo: + ssid: str + strength: int + is_connected: bool + security_type: SecurityType + path: str + bssid: str + # saved_path: str + + +@dataclass +class WifiManagerCallbacks: + need_auth: Callable[[], None] | None = None + activated: Callable[[], None] | None = None + forgotten: Callable[[], None] | None = None + + +class WifiManager: + def __init__(self, callbacks): + self.callbacks: WifiManagerCallbacks = callbacks + self.networks: list[NetworkInfo] = [] + self.bus: MessageBus = None + self.device_path: str = "" + self.device_proxy = None + self.saved_connections: dict[str, str] = {} + self.active_ap_path: str = "" + self.scan_task: asyncio.Task | None = None + self.running: bool = True + + async def connect(self) -> None: + """Connect to the DBus system bus.""" + try: + self.bus = await MessageBus(bus_type=BusType.SYSTEM).connect() + if not await self._find_wifi_device(): + raise ValueError("No Wi-Fi device found") + await self._setup_signals(self.device_path) + + self.active_ap_path = await self.get_active_access_point() + self.saved_connections = await self._get_saved_connections() + self.scan_task = asyncio.create_task(self._periodic_scan()) + except DBusError as e: + cloudlog.error(f"Failed to connect to DBus: {e}") + raise + except Exception as e: + cloudlog.error(f"Unexpected error during connect: {e}") + raise + + async def shutdown(self) -> None: + self.running = False + if self.scan_task: + self.scan_task.cancel() + try: + await self.scan_task + except asyncio.CancelledError: + pass + if self.bus: + await self.bus.disconnect() + + async def request_scan(self) -> None: + try: + interface = self.device_proxy.get_interface(NM_WIRELESS_IFACE) + await interface.call_request_scan({}) + except DBusError as e: + cloudlog.warning(f"Scan request failed: {str(e)}") + + async def get_active_access_point(self): + try: + props_iface = self.device_proxy.get_interface(NM_PROPERTIES_IFACE) + ap_path = await props_iface.call_get(NM_WIRELESS_IFACE, 'ActiveAccessPoint') + return ap_path.value + except DBusError as e: + cloudlog.error(f"Error fetching active access point: {str(e)}") + return '' + + async def forget_connection(self, ssid: str) -> bool: + path = self.saved_connections.get(ssid) + if not path: + return False + + try: + nm_iface = await self._get_interface(NM, path, NM_CONNECTION_IFACE) + await nm_iface.call_delete() + return True + except DBusError as e: + cloudlog.error(f"Failed to delete connection for SSID: {ssid}. Error: {e}") + return False + + async def activate_connection(self, ssid: str) -> bool: + connection_path = self.saved_connections.get(ssid) + if not connection_path: + return False + try: + nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) + await nm_iface.call_activate_connection(connection_path, self.device_path, "/") + return True + except DBusError as e: + cloudlog.error(f"Failed to activate connection {ssid}: {str(e)}") + return False + + async def connect_to_network(self, ssid: str, password: str = None, bssid: str = None, is_hidden: bool = False) -> None: + """Connect to a selected Wi-Fi network.""" + try: + connection = { + 'connection': { + 'type': Variant('s', '802-11-wireless'), + 'uuid': Variant('s', str(uuid.uuid4())), + 'id': Variant('s', ssid), + 'autoconnect-retries': Variant('i', 0), + }, + '802-11-wireless': { + 'ssid': Variant('ay', ssid.encode('utf-8')), + 'hidden': Variant('b', is_hidden), + 'mode': Variant('s', 'infrastructure'), + }, + 'ipv4': {'method': Variant('s', 'auto')}, + 'ipv6': {'method': Variant('s', 'ignore')}, + } + + if bssid: + connection['802-11-wireless']['bssid'] = Variant('ay', bssid.encode('utf-8')) + + if password: + connection['802-11-wireless-security'] = { + 'key-mgmt': Variant('s', 'wpa-psk'), + 'auth-alg': Variant('s', 'open'), + 'psk': Variant('s', password), + } + + nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) + await nm_iface.call_add_and_activate_connection(connection, self.device_path, "/") + await self._update_connection_status() + + except DBusError as e: + cloudlog.error(f"Error connecting to network: {e}") + + def is_saved(self, ssid: str) -> bool: + return ssid in self.saved_connections + + async def _find_wifi_device(self) -> bool: + nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) + devices = await nm_iface.get_devices() + + for device_path in devices: + device = await self.bus.introspect(NM, device_path) + device_proxy = self.bus.get_proxy_object(NM, device_path, device) + device_interface = device_proxy.get_interface(NM_DEVICE_IFACE) + device_type = await device_interface.get_device_type() # type: ignore[attr-defined] + if device_type == 2: # Wi-Fi device + self.device_path = device_path + self.device_proxy = device_proxy + return True + + return False + + async def _periodic_scan(self): + while self.running: + try: + await self.request_scan() + await self._get_available_networks() + await asyncio.sleep(30) + except asyncio.CancelledError: + break + except DBusError as e: + cloudlog.error(f"Scan failed: {e}") + await asyncio.sleep(5) + + async def _setup_signals(self, device_path: str) -> None: + rules = [ + f"type='signal',interface='{NM_PROPERTIES_IFACE}',member='PropertiesChanged',path='{device_path}'", + f"type='signal',interface='{NM_DEVICE_IFACE}',member='StateChanged',path='{device_path}'", + f"type='signal',interface='{NM_SETTINGS_IFACE}',member='NewConnection',path='{NM_SETTINGS_PATH}'", + f"type='signal',interface='{NM_SETTINGS_IFACE}',member='ConnectionRemoved',path='{NM_SETTINGS_PATH}'", + ] + for rule in rules: + await self._add_match_rule(rule) + + # Set up signal handlers + self.device_proxy.get_interface(NM_PROPERTIES_IFACE).on_properties_changed(self._on_properties_changed) + self.device_proxy.get_interface(NM_DEVICE_IFACE).on_state_changed(self._on_state_changed) + + settings_iface = await self._get_interface(NM, NM_SETTINGS_PATH, NM_SETTINGS_IFACE) + settings_iface.on_new_connection(self._on_new_connection) + settings_iface.on_connection_removed(self._on_connection_removed) + + def _on_properties_changed(self, interface: str, changed: dict, invalidated: list): + # print("property changed", interface, changed, invalidated) + if 'LastScan' in changed: + asyncio.create_task(self._get_available_networks()) + elif interface == NM_WIRELESS_IFACE and "ActiveAccessPoint" in changed: + self.active_ap_path = changed["ActiveAccessPoint"].value + asyncio.create_task(self._get_available_networks()) + + def _on_state_changed(self, new_state: int, old_state: int, reason: int): + print(f"State changed: {old_state} -> {new_state}, reason: {reason}") + if new_state == NMDeviceState.ACTIVATED: + if self.callbacks.activated: + self.callbacks.activated() + asyncio.create_task(self._update_connection_status()) + elif new_state in (NMDeviceState.DISCONNECTED, NMDeviceState.NEED_AUTH): + for network in self.networks: + network.is_connected = False + if new_state == NMDeviceState.NEED_AUTH and reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and self.callbacks.need_auth: + self.callbacks.need_auth() + + def _on_new_connection(self, path: str) -> None: + """Callback for NewConnection signal.""" + print(f"New connection added: {path}") + asyncio.create_task(self._add_saved_connection(path)) + + def _on_connection_removed(self, path: str) -> None: + """Callback for ConnectionRemoved signal.""" + print(f"Connection removed: {path}") + for ssid, p in list(self.saved_connections.items()): + if path == p: + del self.saved_connections[ssid] + if self.callbacks.forgotten: + self.callbacks.forgotten() + break + + async def _add_saved_connection(self, path: str) -> None: + """Add a new saved connection to the dictionary.""" + try: + settings = await self._get_connection_settings(path) + if ssid := self._extract_ssid(settings): + self.saved_connections[ssid] = path + except DBusError as e: + cloudlog.error(f"Failed to add connection {path}: {e}") + + def _extract_ssid(self, settings: dict) -> str | None: + """Extract SSID from connection settings.""" + ssid_variant = settings.get('802-11-wireless', {}).get('ssid', Variant('ay', b'')).value + return ''.join(chr(b) for b in ssid_variant) if ssid_variant else None + + async def _update_connection_status(self): + self.active_ap_path = await self.get_active_access_point() + await self._get_available_networks() + + async def _add_match_rule(self, rule): + """Add a match rule on the bus.""" + reply = await self.bus.call( + Message( + message_type=MessageType.METHOD_CALL, + destination='org.freedesktop.DBus', + interface="org.freedesktop.DBus", + path='/org/freedesktop/DBus', + member='AddMatch', + signature='s', + body=[rule], + ) + ) + + assert reply.message_type == MessageType.METHOD_RETURN + return reply + + async def _get_available_networks(self): + """Get a list of available networks via NetworkManager.""" + wifi_iface = self.device_proxy.get_interface(NM_WIRELESS_IFACE) + access_points = await wifi_iface.get_access_points() + network_dict = {} + for ap_path in access_points: + try: + props_iface = await self._get_interface(NM, ap_path, NM_PROPERTIES_IFACE) + properties = await props_iface.call_get_all('org.freedesktop.NetworkManager.AccessPoint') + ssid_variant = properties['Ssid'].value + ssid = ''.join(chr(byte) for byte in ssid_variant) + if not ssid: + continue + + bssid = properties.get('HwAddress', Variant('s', '')).value + strength = properties['Strength'].value + flags = properties['Flags'].value + wpa_flags = properties['WpaFlags'].value + rsn_flags = properties['RsnFlags'].value + existing_network = network_dict.get(ssid) + if not existing_network or ((not existing_network.bssid and bssid) or (existing_network.strength < strength)): + network_dict[ssid] = NetworkInfo( + ssid=ssid, + strength=strength, + security_type=self._get_security_type(flags, wpa_flags, rsn_flags), + path=ap_path, + bssid=bssid, + is_connected=self.active_ap_path == ap_path, + ) + + except DBusError as e: + cloudlog.error(f"Error fetching networks: {e}") + except Exception as e: + cloudlog.error({e}) + + self.networks = sorted( + network_dict.values(), + key=lambda network: ( + not network.is_connected, + -network.strength, # Higher signal strength first + network.ssid.lower(), + ), + ) + + async def _get_connection_settings(self, path): + """Fetch connection settings for a specific connection path.""" + try: + connection_proxy = await self.bus.introspect(NM, path) + connection = self.bus.get_proxy_object(NM, path, connection_proxy) + settings = connection.get_interface(NM_CONNECTION_IFACE) + return await settings.call_get_settings() + except DBusError as e: + cloudlog.error(f"Failed to get settings for {path}: {str(e)}") + return {} + + async def _process_chunk(self, paths_chunk): + """Process a chunk of connection paths.""" + tasks = [self._get_connection_settings(path) for path in paths_chunk] + return await asyncio.gather(*tasks, return_exceptions=True) + + async def _get_saved_connections(self) -> dict[str, str]: + try: + settings_iface = await self._get_interface(NM, NM_SETTINGS_PATH, NM_SETTINGS_IFACE) + connection_paths = await settings_iface.call_list_connections() + saved_ssids: dict[str, str] = {} + batch_size = 20 + for i in range(0, len(connection_paths), batch_size): + chunk = connection_paths[i : i + batch_size] + results = await self._process_chunk(chunk) + for path, config in zip(chunk, results, strict=True): + if isinstance(config, dict) and '802-11-wireless' in config: + if ssid := self._extract_ssid(config): + saved_ssids[ssid] = path + return saved_ssids + except DBusError as e: + cloudlog.error(f"Error fetching saved connections: {str(e)}") + return {} + + async def _get_interface(self, bus_name: str, path: str, name: str): + introspection = await self.bus.introspect(bus_name, path) + proxy = self.bus.get_proxy_object(bus_name, path, introspection) + return proxy.get_interface(name) + + def _get_security_type(self, flags: int, wpa_flags: int, rsn_flags: int) -> SecurityType: + """Determine the security type based on flags.""" + if flags == 0 and not (wpa_flags or rsn_flags): + return SecurityType.OPEN + if rsn_flags & 0x200: # SAE (WPA3 Personal) + return SecurityType.WPA3 + if rsn_flags: # RSN indicates WPA2 or higher + return SecurityType.WPA2 + if wpa_flags: # WPA flags indicate WPA + return SecurityType.WPA + return SecurityType.UNSUPPORTED + + +class WifiManagerWrapper: + def __init__(self): + self._manager: WifiManager | None = None + self._callbacks: WifiManagerCallbacks = WifiManagerCallbacks() + + self._thread = threading.Thread(target=self._run, daemon=True) + self._loop: asyncio.EventLoop | None = None + self._running = False + + def set_callbacks(self, callbacks: WifiManagerCallbacks): + self._callbacks = callbacks + + def start(self) -> None: + if not self._running: + self._thread.start() + while self._thread is not None and not self._running: + time.sleep(0.1) + + def _run(self): + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) + + try: + self._manager = WifiManager(self._callbacks) + self._running = True + self._loop.run_forever() + except Exception as e: + cloudlog.error(f"Error in WifiManagerWrapper thread: {e}") + finally: + if self._loop.is_running(): + self._loop.stop() + self._running = False + + def shutdown(self) -> None: + if self._running: + if self._manager is not None: + self._run_coroutine(self._manager.shutdown()) + if self._loop and self._loop.is_running(): + self._loop.call_soon_threadsafe(self._loop.stop) + if self._thread and self._thread.is_alive(): + self._thread.join(timeout=2.0) + self._running = False + + @property + def networks(self) -> list[NetworkInfo]: + """Get the current list of networks.""" + return self._manager.networks if self._manager else [] + + def is_saved(self, ssid: str) -> bool: + """Check if a network is saved.""" + return self._manager.is_saved(ssid) if self._manager else False + + def connect(self): + """Connect to DBus and start Wi-Fi scanning.""" + if not self._manager: + return + self._run_coroutine(self._manager.connect()) + + def request_scan(self): + """Request a scan for Wi-Fi networks.""" + if not self._manager: + return + self._run_coroutine(self._manager.request_scan()) + + def forget_connection(self, ssid: str): + """Forget a saved Wi-Fi connection.""" + if not self._manager: + return + self._run_coroutine(self._manager.forget_connection(ssid)) + + def activate_connection(self, ssid: str): + """Activate an existing Wi-Fi connection.""" + if not self._manager: + return + self._run_coroutine(self._manager.activate_connection(ssid)) + + def connect_to_network(self, ssid: str, password: str = None, bssid: str = None, is_hidden: bool = False): + """Connect to a Wi-Fi network.""" + if not self._manager: + return + self._run_coroutine(self._manager.connect_to_network(ssid, password, bssid, is_hidden)) + + def _run_coroutine(self, coro): + """Run a coroutine in the async thread.""" + if not self._running or not self._loop: + cloudlog.error("WifiManager thread is not running") + return + asyncio.run_coroutine_threadsafe(coro, self._loop) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 4d1ad1b2cd..13fdb912b5 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -44,25 +44,28 @@ keyboard_layouts = { class Keyboard: def __init__(self, max_text_size: int = 255): self._layout = keyboard_layouts["lowercase"] - self._input_text = "" self._max_text_size = max_text_size + self._string_pointer = rl.ffi.new("char[]", max_text_size) + self._input_text = "" + self._clear() @property - def text(self) -> str: - return self._input_text - - def clear(self): - self._input_text = "" + def text(self): + result = rl.ffi.string(self._string_pointer).decode("utf-8") + self._clear() + return result def render(self, rect, title, sub_title): gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90) gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY) if gui_button(rl.Rectangle(rect.x + rect.width - 300, rect.y, 300, 100), "Cancel"): - return -1 + self._clear() + return 0 # Text box for input - rl.gui_text_box(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100), self._input_text, self._max_text_size, True) - + self._sync_string_pointer() + rl.gui_text_box(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100), self._string_pointer, self._max_text_size, True) + self._input_text = rl.ffi.string(self._string_pointer).decode("utf-8") h_space, v_space = 15, 15 row_y_start = rect.y + 300 # Starting Y position for the first row key_height = (rect.height - 300 - 3 * v_space) / 4 @@ -87,7 +90,7 @@ class Keyboard: else: self.handle_key_press(key) - return 0 + return -1 def handle_key_press(self, key): if key in (SHIFT_DOWN_KEY, ABC_KEY): @@ -102,3 +105,14 @@ class Keyboard: self._input_text = self._input_text[:-1] elif key != BACKSPACE_KEY and len(self._input_text) < self._max_text_size: self._input_text += key + + def _clear(self): + self._input_text = '' + self._string_pointer[0] = b'\0' + + def _sync_string_pointer(self): + """Sync the C-string pointer with the internal Python string.""" + encoded = self._input_text.encode("utf-8")[:self._max_text_size - 1] # Leave room for the null terminator + buffer = rl.ffi.buffer(self._string_pointer) + buffer[:len(encoded)] = encoded + self._string_pointer[len(encoded)] = b'\0' # Null terminator diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py new file mode 100644 index 0000000000..59427b57b8 --- /dev/null +++ b/system/ui/widgets/network.py @@ -0,0 +1,172 @@ +from dataclasses import dataclass +from typing import Literal + +import pyray as rl +from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.label import gui_label +from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.widgets.keyboard import Keyboard +from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog + +NM_DEVICE_STATE_NEED_AUTH = 60 +ITEM_HEIGHT = 160 + + +@dataclass +class StateIdle: + action: Literal["idle"] = "idle" + +@dataclass +class StateConnecting: + network: NetworkInfo + action: Literal["connecting"] = "connecting" + +@dataclass +class StateNeedsAuth: + network: NetworkInfo + action: Literal["needs_auth"] = "needs_auth" + +@dataclass +class StateShowForgetConfirm: + network: NetworkInfo + action: Literal["show_forget_confirm"] = "show_forget_confirm" + +@dataclass +class StateForgetting: + network: NetworkInfo + action: Literal["forgetting"] = "forgetting" + +UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm | StateForgetting + + +class WifiManagerUI: + def __init__(self, wifi_manager: WifiManagerWrapper): + self.state: UIState = StateIdle() + self.btn_width = 200 + self.scroll_panel = GuiScrollPanel() + self.keyboard = Keyboard() + + self.wifi_manager = wifi_manager + self.wifi_manager.set_callbacks(WifiManagerCallbacks(self._on_need_auth, self._on_activated, self._on_forgotten)) + self.wifi_manager.start() + self.wifi_manager.connect() + + def render(self, rect: rl.Rectangle): + if not self.wifi_manager.networks: + gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + return + + match self.state: + case StateNeedsAuth(network): + result = self.keyboard.render(rect, "Enter password", f"for {network.ssid}") + if result == 1: + self.connect_to_network(network, self.keyboard.text) + elif result == 0: + self.state = StateIdle() + + case StateShowForgetConfirm(network): + result = confirm_dialog(rect, f'Forget Wi-Fi Network "{network.ssid}"?', "Forget") + if result == 1: + self.forget_network(network) + elif result == 0: + self.state = StateIdle() + + case _: + self._draw_network_list(rect) + + def _draw_network_list(self, rect: rl.Rectangle): + content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self.wifi_manager.networks) * ITEM_HEIGHT) + offset = self.scroll_panel.handle_scroll(rect, content_rect) + clicked = self.scroll_panel.is_click_valid() + + rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) + for i, network in enumerate(self.wifi_manager.networks): + y_offset = rect.y + i * ITEM_HEIGHT + offset.y + item_rect = rl.Rectangle(rect.x, y_offset, rect.width, ITEM_HEIGHT) + if not rl.check_collision_recs(item_rect, rect): + continue + + self._draw_network_item(item_rect, network, clicked) + if i < len(self.wifi_manager.networks) - 1: + line_y = int(item_rect.y + item_rect.height - 1) + rl.draw_line(int(item_rect.x), int(line_y), int(item_rect.x + item_rect.width), line_y, rl.LIGHTGRAY) + + rl.end_scissor_mode() + + def _draw_network_item(self, rect, network: NetworkInfo, clicked: bool): + label_rect = rl.Rectangle(rect.x, rect.y, rect.width - self.btn_width * 2, ITEM_HEIGHT) + state_rect = rl.Rectangle(rect.x + rect.width - self.btn_width * 2 - 150, rect.y, 300, ITEM_HEIGHT) + + gui_label(label_rect, network.ssid, 55) + + status_text = "" + if network.is_connected: + status_text = "Connected" + match self.state: + case StateConnecting(network=connecting): + if connecting.ssid == network.ssid: + status_text = "CONNECTING..." + case StateForgetting(network=forgetting): + if forgetting.ssid == network.ssid: + status_text = "FORGETTING..." + if status_text: + rl.gui_label(state_rect, status_text) + + # If the network is saved, show the "Forget" button + if self.wifi_manager.is_saved(network.ssid): + forget_btn_rect = rl.Rectangle( + rect.x + rect.width - self.btn_width, + rect.y + (ITEM_HEIGHT - 80) / 2, + self.btn_width, + 80, + ) + if isinstance(self.state, StateIdle) and gui_button(forget_btn_rect, "Forget") and clicked: + self.state = StateShowForgetConfirm(network) + + if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), label_rect) and clicked: + if not self.wifi_manager.is_saved(network.ssid): + self.state = StateNeedsAuth(network) + else: + self.connect_to_network(network) + + def connect_to_network(self, network: NetworkInfo, password=''): + self.state = StateConnecting(network) + if self.wifi_manager.is_saved(network.ssid) and not password: + self.wifi_manager.activate_connection(network.ssid) + else: + self.wifi_manager.connect_to_network(network.ssid, password) + + def forget_network(self, network: NetworkInfo): + self.state = StateForgetting(network) + self.wifi_manager.forget_connection(network.ssid) + + def _on_need_auth(self): + match self.state: + case StateConnecting(network): + self.state = StateNeedsAuth(network) + + def _on_activated(self): + if isinstance(self.state, StateConnecting): + self.state = StateIdle() + + def _on_forgotten(self): + if isinstance(self.state, StateForgetting): + self.state = StateIdle() + + +def main(): + gui_app.init_window("Wi-Fi Manager") + wifi_manager = WifiManagerWrapper() + wifi_ui = WifiManagerUI(wifi_manager) + + for _ in gui_app.render(): + wifi_ui.render(rl.Rectangle(50, 50, gui_app.width - 100, gui_app.height - 100)) + + wifi_manager.shutdown() + gui_app.close() + + +if __name__ == "__main__": + main() From a8b9350103fe76a523a67ceff5719bff46803c39 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 5 May 2025 12:51:04 -0700 Subject: [PATCH 112/477] [bot] Update Python packages (#35125) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- uv.lock | 70 ++++++++++++++++++++++++++-------------------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index b82e8e0122..de651d84e8 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit b82e8e01225a2b9e0953f59d336b9d278bab9acf +Subproject commit de651d84e8514e58aaa6073b06327706b93f5b8a diff --git a/uv.lock b/uv.lock index eb43ede212..1d19dfd22e 100644 --- a/uv.lock +++ b/uv.lock @@ -149,16 +149,16 @@ wheels = [ [[package]] name = "azure-core" -version = "1.33.0" +version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "six" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/aa/7c9db8edd626f1a7d99d09ef7926f6f4fb34d5f9fa00dc394afdfe8e2a80/azure_core-1.33.0.tar.gz", hash = "sha256:f367aa07b5e3005fec2c1e184b882b0b039910733907d001c20fb08ebb8c0eb9", size = 295633, upload-time = "2025-04-03T23:51:02.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/29/ff7a519a315e41c85bab92a7478c6acd1cf0b14353139a08caee4c691f77/azure_core-1.34.0.tar.gz", hash = "sha256:bdb544989f246a0ad1c85d72eeb45f2f835afdcbc5b45e43f0dbde7461c81ece", size = 297999, upload-time = "2025-05-01T23:17:27.59Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/b7/76b7e144aa53bd206bf1ce34fa75350472c3f69bf30e5c8c18bc9881035d/azure_core-1.33.0-py3-none-any.whl", hash = "sha256:9b5b6d0223a1d38c37500e6971118c1e0f13f54951e6893968b38910bc9cda8f", size = 207071, upload-time = "2025-04-03T23:51:03.806Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/5c87b49f65bb16571599bc789857d0ded2f53014d3392bc88a5d1f3ad779/azure_core-1.34.0-py3-none-any.whl", hash = "sha256:0615d3b756beccdb6624d1c0ae97284f38b78fb59a2a9839bf927c66fbbdddd6", size = 207409, upload-time = "2025-05-01T23:17:29.818Z" }, ] [[package]] @@ -260,37 +260,37 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995, upload-time = "2024-12-24T18:10:12.838Z" }, - { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471, upload-time = "2024-12-24T18:10:14.101Z" }, - { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831, upload-time = "2024-12-24T18:10:15.512Z" }, - { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335, upload-time = "2024-12-24T18:10:18.369Z" }, - { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862, upload-time = "2024-12-24T18:10:19.743Z" }, - { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673, upload-time = "2024-12-24T18:10:21.139Z" }, - { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211, upload-time = "2024-12-24T18:10:22.382Z" }, - { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039, upload-time = "2024-12-24T18:10:24.802Z" }, - { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939, upload-time = "2024-12-24T18:10:26.124Z" }, - { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075, upload-time = "2024-12-24T18:10:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340, upload-time = "2024-12-24T18:10:32.679Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205, upload-time = "2024-12-24T18:10:34.724Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441, upload-time = "2024-12-24T18:10:37.574Z" }, - { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, - { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, - { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, - { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, - { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, - { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, - { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, - { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, - { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, - { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, - { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] [[package]] @@ -4814,11 +4814,11 @@ wheels = [ [[package]] name = "setuptools" -version = "80.1.0" +version = "80.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/b2/bd26ed086b842b68c8fe9aac380ad7e5118cf84fa7abd45bb059a88368a8/setuptools-80.1.0.tar.gz", hash = "sha256:2e308396e1d83de287ada2c2fd6e64286008fe6aca5008e0b6a8cb0e2c86eedd", size = 1354038, upload-time = "2025-04-30T17:41:06.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/70/dc/3976b322de9d2e87ed0007cf04cc7553969b6c7b3f48a565d0333748fbcd/setuptools-80.3.1.tar.gz", hash = "sha256:31e2c58dbb67c99c289f51c16d899afedae292b978f8051efaf6262d8212f927", size = 1315082, upload-time = "2025-05-04T18:47:04.397Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/f6/126c9309c8fe93e5d6bb850593cd58d591daf2da45cc78b61e48d8d95879/setuptools-80.1.0-py3-none-any.whl", hash = "sha256:ea0e7655c05b74819f82e76e11a85b31779fee7c4969e82f72bab0664e8317e4", size = 1240689, upload-time = "2025-04-30T17:41:03.789Z" }, + { url = "https://files.pythonhosted.org/packages/53/7e/5d8af3317ddbf9519b687bd1c39d8737fde07d97f54df65553faca5cffb1/setuptools-80.3.1-py3-none-any.whl", hash = "sha256:ea8e00d7992054c4c592aeb892f6ad51fe1b4d90cc6947cc45c45717c40ec537", size = 1201172, upload-time = "2025-05-04T18:47:02.575Z" }, ] [[package]] From 71b37cfb9487a7a48e7f842e6e36a24dbd1a34e5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 6 May 2025 05:33:45 +0800 Subject: [PATCH 113/477] tools/clip: fix SyntaxError in f-string due to unmatched brackets (#35129) fix lint issue --- tools/clip/run.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 2384edc4bb..33c98a0213 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -65,13 +65,13 @@ def get_meta_text(route: Route): origin_parts = metadata['git_remote'].split('/') origin = origin_parts[3] if len(origin_parts) > 3 else 'unknown' return ', '.join([ - f'openpilot v{metadata['version']}', - f'route: {metadata['fullname']}', - f'car: {metadata['platform']}', - f'origin: {origin}', - f'branch: {metadata['git_branch']}', - f'commit: {metadata['git_commit'][:7]}', - f'modified: {str(metadata['git_dirty']).lower()}', + f"openpilot v{metadata['version']}", + f"route: {metadata['fullname']}", + f"car: {metadata['platform']}", + f"origin: {origin}", + f"branch: {metadata['git_branch']}", + f"commit: {metadata['git_commit'][:7]}", + f"modified: {str(metadata['git_dirty']).lower()}", ]) From 5a94d818bb3c77e7da5ebab1213b5979de7b75c9 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 5 May 2025 15:21:06 -0700 Subject: [PATCH 114/477] add xattr python package (#35131) * add xattr * comment --- pyproject.toml | 1 + uv.lock | 1131 +++++++++++++++++++++++++----------------------- 2 files changed, 582 insertions(+), 550 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 344bf2cfcc..6db5f00621 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ dependencies = [ "psutil", "pycryptodome", # used in updated/casync, panda, body, and a test "setproctitle", + "xattr", # used in place of 'os.getxattr' for macos compatibility # logreader "zstandard", diff --git a/uv.lock b/uv.lock index 1d19dfd22e..fbeda4d552 100644 --- a/uv.lock +++ b/uv.lock @@ -503,7 +503,7 @@ version = "0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-xlib", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657, upload-time = "2024-04-17T08:15:56.338Z" }, @@ -650,10 +650,10 @@ name = "gymnasium" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cloudpickle" }, - { name = "farama-notifications" }, - { name = "numpy" }, - { name = "typing-extensions" }, + { name = "cloudpickle", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "farama-notifications", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326, upload-time = "2025-03-06T16:30:36.428Z" } wheels = [ @@ -964,22 +964,22 @@ name = "metadrive-simulator" version = "0.4.2.4" source = { url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl" } dependencies = [ - { name = "filelock" }, - { name = "gymnasium" }, - { name = "lxml" }, - { name = "matplotlib" }, - { name = "numpy" }, - { name = "opencv-python-headless" }, - { name = "panda3d" }, - { name = "panda3d-gltf" }, - { name = "pillow" }, - { name = "progressbar" }, - { name = "psutil" }, - { name = "pygments" }, - { name = "requests" }, - { name = "shapely" }, - { name = "tqdm" }, - { name = "yapf" }, + { name = "filelock", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "gymnasium", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "lxml", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "matplotlib", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "opencv-python-headless", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d-gltf", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "pillow", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "progressbar", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "psutil", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "pygments", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "requests", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "shapely", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "tqdm", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "yapf", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] wheels = [ { url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl", hash = "sha256:fbf0ea9be67e65cd45d38ff930e3d49f705dd76c9ddbd1e1482e3f87b61efcef" }, @@ -1238,7 +1238,7 @@ name = "opencv-python-headless" version = "4.11.0.86" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } wheels = [ @@ -1287,6 +1287,7 @@ dependencies = [ { name = "sympy" }, { name = "tqdm" }, { name = "websocket-client" }, + { name = "xattr" }, { name = "zstandard" }, ] @@ -1412,6 +1413,7 @@ requires-dist = [ { name = "types-requests", marker = "extra == 'dev'" }, { name = "types-tabulate", marker = "extra == 'dev'" }, { name = "websocket-client" }, + { name = "xattr" }, { name = "zstandard" }, ] provides-extras = ["docs", "testing", "dev", "tools"] @@ -1448,8 +1450,8 @@ name = "panda3d-gltf" version = "0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d" }, - { name = "panda3d-simplepbr" }, + { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d-simplepbr", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ @@ -1461,8 +1463,8 @@ name = "panda3d-simplepbr" version = "0.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d" }, - { name = "typing-extensions" }, + { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ @@ -1860,162 +1862,162 @@ name = "pyobjc" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-accessibility", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-accounts", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-addressbook" }, - { name = "pyobjc-framework-adservices", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-adsupport", marker = "platform_release >= '18.0'" }, - { name = "pyobjc-framework-applescriptkit" }, - { name = "pyobjc-framework-applescriptobjc", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-applicationservices" }, - { name = "pyobjc-framework-apptrackingtransparency", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-audiovideobridging", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-authenticationservices", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-automaticassessmentconfiguration", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-automator" }, - { name = "pyobjc-framework-avfoundation", marker = "platform_release >= '11.0'" }, - { name = "pyobjc-framework-avkit", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-avrouting", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-backgroundassets", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-browserenginekit", marker = "platform_release >= '23.4'" }, - { name = "pyobjc-framework-businesschat", marker = "platform_release >= '18.0'" }, - { name = "pyobjc-framework-calendarstore", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-callkit", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-carbon" }, - { name = "pyobjc-framework-cfnetwork" }, - { name = "pyobjc-framework-cinematic", marker = "platform_release >= '23.0'" }, - { name = "pyobjc-framework-classkit", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-cloudkit", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-collaboration", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-colorsync", marker = "platform_release >= '17.0'" }, - { name = "pyobjc-framework-contacts", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-contactsui", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-coreaudio" }, - { name = "pyobjc-framework-coreaudiokit" }, - { name = "pyobjc-framework-corebluetooth", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-coredata" }, - { name = "pyobjc-framework-corehaptics", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-corelocation", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-coremedia", marker = "platform_release >= '11.0'" }, - { name = "pyobjc-framework-coremediaio", marker = "platform_release >= '11.0'" }, - { name = "pyobjc-framework-coremidi" }, - { name = "pyobjc-framework-coreml", marker = "platform_release >= '17.0'" }, - { name = "pyobjc-framework-coremotion", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-coreservices" }, - { name = "pyobjc-framework-corespotlight", marker = "platform_release >= '17.0'" }, - { name = "pyobjc-framework-coretext" }, - { name = "pyobjc-framework-corewlan", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-cryptotokenkit", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-datadetection", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-devicecheck", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-devicediscoveryextension", marker = "platform_release >= '24.0'" }, - { name = "pyobjc-framework-dictionaryservices", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-discrecording" }, - { name = "pyobjc-framework-discrecordingui" }, - { name = "pyobjc-framework-diskarbitration" }, - { name = "pyobjc-framework-dvdplayback" }, - { name = "pyobjc-framework-eventkit", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-exceptionhandling" }, - { name = "pyobjc-framework-executionpolicy", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-extensionkit", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-externalaccessory", marker = "platform_release >= '17.0'" }, - { name = "pyobjc-framework-fileprovider", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-fileproviderui", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-findersync", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-fsevents", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-gamecenter", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-gamecontroller", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-gamekit", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-gameplaykit", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-healthkit", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-imagecapturecore", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-inputmethodkit", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-installerplugins" }, - { name = "pyobjc-framework-instantmessage", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-intents", marker = "platform_release >= '16.0'" }, - { name = "pyobjc-framework-intentsui", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-iobluetooth" }, - { name = "pyobjc-framework-iobluetoothui" }, - { name = "pyobjc-framework-iosurface", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-ituneslibrary", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-kernelmanagement", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-latentsemanticmapping" }, - { name = "pyobjc-framework-launchservices" }, - { name = "pyobjc-framework-libdispatch", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-libxpc", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-linkpresentation", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-localauthentication", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-localauthenticationembeddedui", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-mailkit", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-mapkit", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-mediaaccessibility", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-mediaextension", marker = "platform_release >= '24.0'" }, - { name = "pyobjc-framework-medialibrary", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-mediaplayer", marker = "platform_release >= '16.0'" }, - { name = "pyobjc-framework-mediatoolbox", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-metal", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-metalfx", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-metalkit", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-metalperformanceshaders", marker = "platform_release >= '17.0'" }, - { name = "pyobjc-framework-metalperformanceshadersgraph", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-metrickit", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-mlcompute", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-modelio", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-multipeerconnectivity", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-naturallanguage", marker = "platform_release >= '18.0'" }, - { name = "pyobjc-framework-netfs", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-network", marker = "platform_release >= '18.0'" }, - { name = "pyobjc-framework-networkextension", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-notificationcenter", marker = "platform_release >= '14.0'" }, - { name = "pyobjc-framework-opendirectory", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-osakit" }, - { name = "pyobjc-framework-oslog", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-passkit", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-pencilkit", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-phase", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-photos", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-photosui", marker = "platform_release >= '15.0'" }, - { name = "pyobjc-framework-preferencepanes" }, - { name = "pyobjc-framework-pushkit", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-quartz" }, - { name = "pyobjc-framework-quicklookthumbnailing", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-replaykit", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-safariservices", marker = "platform_release >= '16.0'" }, - { name = "pyobjc-framework-safetykit", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-scenekit", marker = "platform_release >= '11.0'" }, - { name = "pyobjc-framework-screencapturekit", marker = "platform_release >= '21.4'" }, - { name = "pyobjc-framework-screensaver" }, - { name = "pyobjc-framework-screentime", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-scriptingbridge", marker = "platform_release >= '9.0'" }, - { name = "pyobjc-framework-searchkit" }, - { name = "pyobjc-framework-security" }, - { name = "pyobjc-framework-securityfoundation" }, - { name = "pyobjc-framework-securityinterface" }, - { name = "pyobjc-framework-sensitivecontentanalysis", marker = "platform_release >= '23.0'" }, - { name = "pyobjc-framework-servicemanagement", marker = "platform_release >= '10.0'" }, - { name = "pyobjc-framework-sharedwithyou", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-sharedwithyoucore", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-shazamkit", marker = "platform_release >= '21.0'" }, - { name = "pyobjc-framework-social", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-soundanalysis", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-speech", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-spritekit", marker = "platform_release >= '13.0'" }, - { name = "pyobjc-framework-storekit", marker = "platform_release >= '11.0'" }, - { name = "pyobjc-framework-symbols", marker = "platform_release >= '23.0'" }, - { name = "pyobjc-framework-syncservices" }, - { name = "pyobjc-framework-systemconfiguration" }, - { name = "pyobjc-framework-systemextensions", marker = "platform_release >= '19.0'" }, - { name = "pyobjc-framework-threadnetwork", marker = "platform_release >= '22.0'" }, - { name = "pyobjc-framework-uniformtypeidentifiers", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-usernotifications", marker = "platform_release >= '18.0'" }, - { name = "pyobjc-framework-usernotificationsui", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-videosubscriberaccount", marker = "platform_release >= '18.0'" }, - { name = "pyobjc-framework-videotoolbox", marker = "platform_release >= '12.0'" }, - { name = "pyobjc-framework-virtualization", marker = "platform_release >= '20.0'" }, - { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0'" }, - { name = "pyobjc-framework-webkit" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-accessibility", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-accounts", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-addressbook", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-adservices", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-adsupport", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-applescriptkit", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-applescriptobjc", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-apptrackingtransparency", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-audiovideobridging", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-authenticationservices", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-automaticassessmentconfiguration", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-automator", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avfoundation", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avkit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avrouting", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-backgroundassets", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-browserenginekit", marker = "platform_release >= '23.4' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-businesschat", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-calendarstore", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-callkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-carbon", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cfnetwork", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cinematic", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-classkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cloudkit", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-collaboration", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-colorsync", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-contacts", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-contactsui", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreaudiokit", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corebluetooth", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corehaptics", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corelocation", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremediaio", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremidi", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreml", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremotion", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corespotlight", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coretext", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corewlan", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cryptotokenkit", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-datadetection", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-devicecheck", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-devicediscoveryextension", marker = "platform_release >= '24.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-dictionaryservices", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-discrecording", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-discrecordingui", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-diskarbitration", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-dvdplayback", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-eventkit", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-exceptionhandling", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-executionpolicy", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-extensionkit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-externalaccessory", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-fileprovider", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-fileproviderui", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-findersync", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-fsevents", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-gamecenter", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-gamecontroller", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-gamekit", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-gameplaykit", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-healthkit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-imagecapturecore", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-inputmethodkit", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-installerplugins", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-instantmessage", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-intents", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-intentsui", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-iobluetooth", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-iobluetoothui", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-iosurface", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-ituneslibrary", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-kernelmanagement", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-latentsemanticmapping", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-launchservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-libdispatch", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-libxpc", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-linkpresentation", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-localauthentication", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-localauthenticationembeddedui", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mailkit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mapkit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mediaaccessibility", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mediaextension", marker = "platform_release >= '24.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-medialibrary", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mediaplayer", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mediatoolbox", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metal", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metalfx", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metalkit", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metalperformanceshaders", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metalperformanceshadersgraph", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metrickit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-mlcompute", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-modelio", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-multipeerconnectivity", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-naturallanguage", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-netfs", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-network", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-networkextension", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-notificationcenter", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-opendirectory", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-osakit", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-oslog", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-passkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-pencilkit", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-phase", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-photos", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-photosui", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-preferencepanes", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-pushkit", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quicklookthumbnailing", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-replaykit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-safariservices", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-safetykit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-scenekit", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-screencapturekit", marker = "platform_release >= '21.4' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-screensaver", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-screentime", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-scriptingbridge", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-searchkit", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-securityfoundation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-securityinterface", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-sensitivecontentanalysis", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-servicemanagement", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-sharedwithyou", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-sharedwithyoucore", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-shazamkit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-social", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-soundanalysis", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-speech", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-spritekit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-storekit", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-symbols", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-syncservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-systemconfiguration", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-systemextensions", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-threadnetwork", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-uniformtypeidentifiers", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-usernotifications", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-usernotificationsui", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-videosubscriberaccount", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-videotoolbox", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-virtualization", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, + { name = "pyobjc-framework-webkit", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044, upload-time = "2025-01-14T19:02:12.55Z" } wheels = [ @@ -2037,9 +2039,9 @@ name = "pyobjc-framework-accessibility" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597, upload-time = "2025-01-14T19:02:17.596Z" } wheels = [ @@ -2052,8 +2054,8 @@ name = "pyobjc-framework-accounts" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340, upload-time = "2025-01-14T19:02:18.625Z" } wheels = [ @@ -2066,8 +2068,8 @@ name = "pyobjc-framework-addressbook" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116, upload-time = "2025-01-14T19:02:19.527Z" } wheels = [ @@ -2080,8 +2082,8 @@ name = "pyobjc-framework-adservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732, upload-time = "2025-01-14T19:02:20.559Z" } wheels = [ @@ -2094,8 +2096,8 @@ name = "pyobjc-framework-adsupport" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819, upload-time = "2025-01-14T19:02:23.032Z" } wheels = [ @@ -2108,8 +2110,8 @@ name = "pyobjc-framework-applescriptkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638, upload-time = "2025-01-14T19:02:25.1Z" } wheels = [ @@ -2122,8 +2124,8 @@ name = "pyobjc-framework-applescriptobjc" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675, upload-time = "2025-01-14T19:02:25.947Z" } wheels = [ @@ -2136,10 +2138,10 @@ name = "pyobjc-framework-applicationservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coretext" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coretext", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334, upload-time = "2025-01-14T19:02:26.828Z" } wheels = [ @@ -2152,8 +2154,8 @@ name = "pyobjc-framework-apptrackingtransparency" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385, upload-time = "2025-01-14T19:02:29.226Z" } wheels = [ @@ -2166,8 +2168,8 @@ name = "pyobjc-framework-audiovideobridging" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418, upload-time = "2025-01-14T19:02:30.083Z" } wheels = [ @@ -2180,8 +2182,8 @@ name = "pyobjc-framework-authenticationservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036, upload-time = "2025-01-14T19:02:31.115Z" } wheels = [ @@ -2194,8 +2196,8 @@ name = "pyobjc-framework-automaticassessmentconfiguration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420, upload-time = "2025-01-14T19:02:32.101Z" } wheels = [ @@ -2208,8 +2210,8 @@ name = "pyobjc-framework-automator" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905, upload-time = "2025-01-14T19:02:33.039Z" } wheels = [ @@ -2222,11 +2224,11 @@ name = "pyobjc-framework-avfoundation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coreaudio" }, - { name = "pyobjc-framework-coremedia" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064, upload-time = "2025-01-14T19:02:35.757Z" } wheels = [ @@ -2239,9 +2241,9 @@ name = "pyobjc-framework-avkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311, upload-time = "2025-01-14T19:02:37.018Z" } wheels = [ @@ -2254,8 +2256,8 @@ name = "pyobjc-framework-avrouting" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561, upload-time = "2025-01-14T19:02:38.157Z" } wheels = [ @@ -2268,8 +2270,8 @@ name = "pyobjc-framework-backgroundassets" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658, upload-time = "2025-01-14T19:02:42.665Z" } wheels = [ @@ -2282,11 +2284,11 @@ name = "pyobjc-framework-browserenginekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coreaudio" }, - { name = "pyobjc-framework-coremedia" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352, upload-time = "2025-01-14T19:02:45.499Z" } wheels = [ @@ -2299,8 +2301,8 @@ name = "pyobjc-framework-businesschat" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953, upload-time = "2025-01-14T19:02:46.378Z" } wheels = [ @@ -2313,8 +2315,8 @@ name = "pyobjc-framework-calendarstore" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475, upload-time = "2025-01-14T19:02:48.544Z" } wheels = [ @@ -2327,8 +2329,8 @@ name = "pyobjc-framework-callkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720, upload-time = "2025-01-14T19:02:50.697Z" } wheels = [ @@ -2341,8 +2343,8 @@ name = "pyobjc-framework-carbon" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538, upload-time = "2025-01-14T19:02:51.62Z" } wheels = [ @@ -2355,8 +2357,8 @@ name = "pyobjc-framework-cfnetwork" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532, upload-time = "2025-01-14T19:02:52.777Z" } wheels = [ @@ -2369,11 +2371,11 @@ name = "pyobjc-framework-cinematic" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-avfoundation" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coremedia" }, - { name = "pyobjc-framework-metal" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740, upload-time = "2025-01-14T19:02:54.95Z" } wheels = [ @@ -2386,8 +2388,8 @@ name = "pyobjc-framework-classkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301, upload-time = "2025-01-14T19:02:55.779Z" } wheels = [ @@ -2400,11 +2402,11 @@ name = "pyobjc-framework-cloudkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-accounts" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coredata" }, - { name = "pyobjc-framework-corelocation" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-accounts", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corelocation", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034, upload-time = "2025-01-14T19:02:56.769Z" } wheels = [ @@ -2417,7 +2419,7 @@ name = "pyobjc-framework-cocoa" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848, upload-time = "2025-01-14T19:03:00.125Z" } wheels = [ @@ -2430,8 +2432,8 @@ name = "pyobjc-framework-collaboration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084, upload-time = "2025-01-14T19:03:01.98Z" } wheels = [ @@ -2444,8 +2446,8 @@ name = "pyobjc-framework-colorsync" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249, upload-time = "2025-01-14T19:03:02.887Z" } wheels = [ @@ -2458,8 +2460,8 @@ name = "pyobjc-framework-contacts" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253, upload-time = "2025-01-14T19:03:03.743Z" } wheels = [ @@ -2472,9 +2474,9 @@ name = "pyobjc-framework-contactsui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-contacts" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-contacts", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486, upload-time = "2025-01-14T19:03:04.72Z" } wheels = [ @@ -2487,8 +2489,8 @@ name = "pyobjc-framework-coreaudio" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507, upload-time = "2025-01-14T19:03:05.612Z" } wheels = [ @@ -2501,9 +2503,9 @@ name = "pyobjc-framework-coreaudiokit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coreaudio" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450, upload-time = "2025-01-14T19:03:06.681Z" } wheels = [ @@ -2516,8 +2518,8 @@ name = "pyobjc-framework-corebluetooth" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797, upload-time = "2025-01-14T19:03:07.584Z" } wheels = [ @@ -2530,8 +2532,8 @@ name = "pyobjc-framework-coredata" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029, upload-time = "2025-01-14T19:03:08.609Z" } wheels = [ @@ -2544,8 +2546,8 @@ name = "pyobjc-framework-corehaptics" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956, upload-time = "2025-01-14T19:03:09.753Z" } wheels = [ @@ -2558,8 +2560,8 @@ name = "pyobjc-framework-corelocation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955, upload-time = "2025-01-14T19:03:10.707Z" } wheels = [ @@ -2572,8 +2574,8 @@ name = "pyobjc-framework-coremedia" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161, upload-time = "2025-01-14T19:03:12.291Z" } wheels = [ @@ -2586,8 +2588,8 @@ name = "pyobjc-framework-coremediaio" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856, upload-time = "2025-01-14T19:03:14.225Z" } wheels = [ @@ -2600,8 +2602,8 @@ name = "pyobjc-framework-coremidi" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342, upload-time = "2025-01-14T19:03:15.235Z" } wheels = [ @@ -2614,8 +2616,8 @@ name = "pyobjc-framework-coreml" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452, upload-time = "2025-01-14T19:03:16.283Z" } wheels = [ @@ -2628,8 +2630,8 @@ name = "pyobjc-framework-coremotion" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508, upload-time = "2025-01-14T19:03:17.254Z" } wheels = [ @@ -2642,9 +2644,9 @@ name = "pyobjc-framework-coreservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-fsevents" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-fsevents", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406, upload-time = "2025-01-14T19:03:19.202Z" } wheels = [ @@ -2657,8 +2659,8 @@ name = "pyobjc-framework-corespotlight" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130, upload-time = "2025-01-14T19:03:20.457Z" } wheels = [ @@ -2671,9 +2673,9 @@ name = "pyobjc-framework-coretext" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222, upload-time = "2025-01-14T19:03:21.521Z" } wheels = [ @@ -2686,8 +2688,8 @@ name = "pyobjc-framework-corewlan" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254, upload-time = "2025-01-14T19:03:23.938Z" } wheels = [ @@ -2700,8 +2702,8 @@ name = "pyobjc-framework-cryptotokenkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734, upload-time = "2025-01-14T19:03:24.851Z" } wheels = [ @@ -2714,8 +2716,8 @@ name = "pyobjc-framework-datadetection" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738, upload-time = "2025-01-14T19:03:27.054Z" } wheels = [ @@ -2728,8 +2730,8 @@ name = "pyobjc-framework-devicecheck" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198, upload-time = "2025-01-14T19:03:27.918Z" } wheels = [ @@ -2742,8 +2744,8 @@ name = "pyobjc-framework-devicediscoveryextension" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511, upload-time = "2025-01-14T19:03:32.132Z" } wheels = [ @@ -2756,8 +2758,8 @@ name = "pyobjc-framework-dictionaryservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-coreservices" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823, upload-time = "2025-01-14T19:03:32.942Z" } wheels = [ @@ -2770,8 +2772,8 @@ name = "pyobjc-framework-discrecording" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426, upload-time = "2025-01-14T19:03:35.589Z" } wheels = [ @@ -2784,9 +2786,9 @@ name = "pyobjc-framework-discrecordingui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-discrecording" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-discrecording", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614, upload-time = "2025-01-14T19:03:36.695Z" } wheels = [ @@ -2799,8 +2801,8 @@ name = "pyobjc-framework-diskarbitration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096, upload-time = "2025-01-14T19:03:37.659Z" } wheels = [ @@ -2813,8 +2815,8 @@ name = "pyobjc-framework-dvdplayback" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798, upload-time = "2025-01-14T19:03:38.491Z" } wheels = [ @@ -2827,8 +2829,8 @@ name = "pyobjc-framework-eventkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399, upload-time = "2025-01-14T19:03:39.441Z" } wheels = [ @@ -2841,8 +2843,8 @@ name = "pyobjc-framework-exceptionhandling" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157, upload-time = "2025-01-14T19:03:40.393Z" } wheels = [ @@ -2855,8 +2857,8 @@ name = "pyobjc-framework-executionpolicy" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753, upload-time = "2025-01-14T19:03:42.353Z" } wheels = [ @@ -2869,8 +2871,8 @@ name = "pyobjc-framework-extensionkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200, upload-time = "2025-01-14T19:03:43.188Z" } wheels = [ @@ -2883,8 +2885,8 @@ name = "pyobjc-framework-externalaccessory" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577, upload-time = "2025-01-14T19:03:44.021Z" } wheels = [ @@ -2897,8 +2899,8 @@ name = "pyobjc-framework-fileprovider" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701, upload-time = "2025-01-14T19:03:44.931Z" } wheels = [ @@ -2911,8 +2913,8 @@ name = "pyobjc-framework-fileproviderui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-fileprovider" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-fileprovider", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419, upload-time = "2025-01-14T19:03:46.016Z" } wheels = [ @@ -2925,8 +2927,8 @@ name = "pyobjc-framework-findersync" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295, upload-time = "2025-01-14T19:03:46.91Z" } wheels = [ @@ -2939,8 +2941,8 @@ name = "pyobjc-framework-fsevents" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078, upload-time = "2025-01-14T19:03:49.762Z" } wheels = [ @@ -2953,8 +2955,8 @@ name = "pyobjc-framework-gamecenter" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459, upload-time = "2025-01-14T19:03:50.766Z" } wheels = [ @@ -2967,8 +2969,8 @@ name = "pyobjc-framework-gamecontroller" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318, upload-time = "2025-01-14T19:03:52.264Z" } wheels = [ @@ -2981,9 +2983,9 @@ name = "pyobjc-framework-gamekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394, upload-time = "2025-01-14T19:03:53.762Z" } wheels = [ @@ -2996,9 +2998,9 @@ name = "pyobjc-framework-gameplaykit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-spritekit" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-spritekit", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837, upload-time = "2025-01-14T19:03:56.127Z" } wheels = [ @@ -3011,8 +3013,8 @@ name = "pyobjc-framework-healthkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558, upload-time = "2025-01-14T19:03:57.117Z" } wheels = [ @@ -3025,8 +3027,8 @@ name = "pyobjc-framework-imagecapturecore" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935, upload-time = "2025-01-14T19:03:58.548Z" } wheels = [ @@ -3039,8 +3041,8 @@ name = "pyobjc-framework-inputmethodkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684, upload-time = "2025-01-14T19:03:59.525Z" } wheels = [ @@ -3053,8 +3055,8 @@ name = "pyobjc-framework-installerplugins" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687, upload-time = "2025-01-14T19:04:00.515Z" } wheels = [ @@ -3067,9 +3069,9 @@ name = "pyobjc-framework-instantmessage" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027, upload-time = "2025-01-14T19:04:01.405Z" } wheels = [ @@ -3082,8 +3084,8 @@ name = "pyobjc-framework-intents" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921, upload-time = "2025-01-14T19:04:02.487Z" } wheels = [ @@ -3096,8 +3098,8 @@ name = "pyobjc-framework-intentsui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-intents" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-intents", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774, upload-time = "2025-01-14T19:04:03.648Z" } wheels = [ @@ -3110,8 +3112,8 @@ name = "pyobjc-framework-iobluetooth" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463, upload-time = "2025-01-14T19:04:04.582Z" } wheels = [ @@ -3124,8 +3126,8 @@ name = "pyobjc-framework-iobluetoothui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-iobluetooth" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-iobluetooth", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091, upload-time = "2025-01-14T19:04:05.659Z" } wheels = [ @@ -3138,8 +3140,8 @@ name = "pyobjc-framework-iosurface" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555, upload-time = "2025-01-14T19:04:09.475Z" } wheels = [ @@ -3152,8 +3154,8 @@ name = "pyobjc-framework-ituneslibrary" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647, upload-time = "2025-01-14T19:04:11.333Z" } wheels = [ @@ -3166,8 +3168,8 @@ name = "pyobjc-framework-kernelmanagement" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794, upload-time = "2025-01-14T19:04:14.204Z" } wheels = [ @@ -3180,8 +3182,8 @@ name = "pyobjc-framework-latentsemanticmapping" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704, upload-time = "2025-01-14T19:04:14.972Z" } wheels = [ @@ -3194,8 +3196,8 @@ name = "pyobjc-framework-launchservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-coreservices" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691, upload-time = "2025-01-14T19:04:15.884Z" } wheels = [ @@ -3208,8 +3210,8 @@ name = "pyobjc-framework-libdispatch" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496, upload-time = "2025-01-14T19:04:16.82Z" } wheels = [ @@ -3222,8 +3224,8 @@ name = "pyobjc-framework-libxpc" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627, upload-time = "2025-01-14T19:04:17.728Z" } wheels = [ @@ -3236,9 +3238,9 @@ name = "pyobjc-framework-linkpresentation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231, upload-time = "2025-01-14T19:04:20.763Z" } wheels = [ @@ -3251,9 +3253,9 @@ name = "pyobjc-framework-localauthentication" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-security" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020, upload-time = "2025-01-14T19:04:22.206Z" } wheels = [ @@ -3266,9 +3268,9 @@ name = "pyobjc-framework-localauthenticationembeddedui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-localauthentication" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-localauthentication", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204, upload-time = "2025-01-14T19:04:23.566Z" } wheels = [ @@ -3281,8 +3283,8 @@ name = "pyobjc-framework-mailkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274, upload-time = "2025-01-14T19:04:25.086Z" } wheels = [ @@ -3295,10 +3297,10 @@ name = "pyobjc-framework-mapkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-corelocation" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corelocation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080, upload-time = "2025-01-14T19:04:26.606Z" } wheels = [ @@ -3311,8 +3313,8 @@ name = "pyobjc-framework-mediaaccessibility" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671, upload-time = "2025-01-14T19:04:27.624Z" } wheels = [ @@ -3325,10 +3327,10 @@ name = "pyobjc-framework-mediaextension" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-avfoundation" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931, upload-time = "2025-01-14T19:04:28.65Z" } wheels = [ @@ -3341,9 +3343,9 @@ name = "pyobjc-framework-medialibrary" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975, upload-time = "2025-01-14T19:04:29.739Z" } wheels = [ @@ -3356,8 +3358,8 @@ name = "pyobjc-framework-mediaplayer" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-avfoundation" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020, upload-time = "2025-01-14T19:04:30.617Z" } wheels = [ @@ -3370,8 +3372,8 @@ name = "pyobjc-framework-mediatoolbox" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067, upload-time = "2025-01-14T19:04:32.823Z" } wheels = [ @@ -3384,8 +3386,8 @@ name = "pyobjc-framework-metal" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102, upload-time = "2025-01-14T19:04:34.011Z" } wheels = [ @@ -3398,8 +3400,8 @@ name = "pyobjc-framework-metalfx" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-metal" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436, upload-time = "2025-01-14T19:04:36.161Z" } wheels = [ @@ -3412,9 +3414,9 @@ name = "pyobjc-framework-metalkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-metal" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628, upload-time = "2025-01-14T19:04:36.977Z" } wheels = [ @@ -3427,8 +3429,8 @@ name = "pyobjc-framework-metalperformanceshaders" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-metal" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444, upload-time = "2025-01-14T19:04:38.064Z" } wheels = [ @@ -3441,8 +3443,8 @@ name = "pyobjc-framework-metalperformanceshadersgraph" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-metalperformanceshaders" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-metalperformanceshaders", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381, upload-time = "2025-01-14T19:04:39.831Z" } wheels = [ @@ -3455,8 +3457,8 @@ name = "pyobjc-framework-metrickit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414, upload-time = "2025-01-14T19:04:41.186Z" } wheels = [ @@ -3469,8 +3471,8 @@ name = "pyobjc-framework-mlcompute" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265, upload-time = "2025-01-14T19:04:43.326Z" } wheels = [ @@ -3483,9 +3485,9 @@ name = "pyobjc-framework-modelio" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652, upload-time = "2025-01-14T19:04:44.263Z" } wheels = [ @@ -3498,8 +3500,8 @@ name = "pyobjc-framework-multipeerconnectivity" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534, upload-time = "2025-01-14T19:04:45.211Z" } wheels = [ @@ -3512,8 +3514,8 @@ name = "pyobjc-framework-naturallanguage" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385, upload-time = "2025-01-14T19:04:46.185Z" } wheels = [ @@ -3526,8 +3528,8 @@ name = "pyobjc-framework-netfs" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173, upload-time = "2025-01-14T19:04:47.11Z" } wheels = [ @@ -3540,8 +3542,8 @@ name = "pyobjc-framework-network" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160, upload-time = "2025-01-14T19:04:50.191Z" } wheels = [ @@ -3554,8 +3556,8 @@ name = "pyobjc-framework-networkextension" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551, upload-time = "2025-01-14T19:04:51.352Z" } wheels = [ @@ -3568,8 +3570,8 @@ name = "pyobjc-framework-notificationcenter" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844, upload-time = "2025-01-14T19:04:52.459Z" } wheels = [ @@ -3582,8 +3584,8 @@ name = "pyobjc-framework-opendirectory" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165, upload-time = "2025-01-14T19:04:53.42Z" } wheels = [ @@ -3596,8 +3598,8 @@ name = "pyobjc-framework-osakit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535, upload-time = "2025-01-14T19:04:54.753Z" } wheels = [ @@ -3610,10 +3612,10 @@ name = "pyobjc-framework-oslog" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coremedia" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151, upload-time = "2025-01-14T19:04:55.587Z" } wheels = [ @@ -3626,8 +3628,8 @@ name = "pyobjc-framework-passkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495, upload-time = "2025-01-14T19:04:57.689Z" } wheels = [ @@ -3640,8 +3642,8 @@ name = "pyobjc-framework-pencilkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812, upload-time = "2025-01-14T19:04:58.652Z" } wheels = [ @@ -3654,8 +3656,8 @@ name = "pyobjc-framework-phase" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-avfoundation" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214, upload-time = "2025-01-14T19:04:59.461Z" } wheels = [ @@ -3668,8 +3670,8 @@ name = "pyobjc-framework-photos" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122, upload-time = "2025-01-14T19:05:01.804Z" } wheels = [ @@ -3682,8 +3684,8 @@ name = "pyobjc-framework-photosui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898, upload-time = "2025-01-14T19:05:02.737Z" } wheels = [ @@ -3696,8 +3698,8 @@ name = "pyobjc-framework-preferencepanes" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419, upload-time = "2025-01-14T19:05:03.787Z" } wheels = [ @@ -3710,8 +3712,8 @@ name = "pyobjc-framework-pushkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816, upload-time = "2025-01-14T19:05:05.468Z" } wheels = [ @@ -3724,8 +3726,8 @@ name = "pyobjc-framework-quartz" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463, upload-time = "2025-01-14T19:05:07.931Z" } wheels = [ @@ -3738,9 +3740,9 @@ name = "pyobjc-framework-quicklookthumbnailing" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784, upload-time = "2025-01-14T19:05:09.857Z" } wheels = [ @@ -3753,8 +3755,8 @@ name = "pyobjc-framework-replaykit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589, upload-time = "2025-01-14T19:05:10.791Z" } wheels = [ @@ -3767,8 +3769,8 @@ name = "pyobjc-framework-safariservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367, upload-time = "2025-01-14T19:05:12.914Z" } wheels = [ @@ -3781,9 +3783,9 @@ name = "pyobjc-framework-safetykit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745, upload-time = "2025-01-14T19:05:13.925Z" } wheels = [ @@ -3796,9 +3798,9 @@ name = "pyobjc-framework-scenekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647, upload-time = "2025-01-14T19:05:15.129Z" } wheels = [ @@ -3811,9 +3813,9 @@ name = "pyobjc-framework-screencapturekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046, upload-time = "2025-01-14T19:05:16.834Z" } wheels = [ @@ -3826,8 +3828,8 @@ name = "pyobjc-framework-screensaver" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774, upload-time = "2025-01-14T19:05:19.325Z" } wheels = [ @@ -3840,8 +3842,8 @@ name = "pyobjc-framework-screentime" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398, upload-time = "2025-01-14T19:05:21.547Z" } wheels = [ @@ -3854,8 +3856,8 @@ name = "pyobjc-framework-scriptingbridge" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626, upload-time = "2025-01-14T19:05:22.461Z" } wheels = [ @@ -3868,8 +3870,8 @@ name = "pyobjc-framework-searchkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-coreservices" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182, upload-time = "2025-01-14T19:05:24.667Z" } wheels = [ @@ -3882,8 +3884,8 @@ name = "pyobjc-framework-security" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059, upload-time = "2025-01-14T19:05:26.17Z" } wheels = [ @@ -3896,9 +3898,9 @@ name = "pyobjc-framework-securityfoundation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-security" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526, upload-time = "2025-01-14T19:05:27.275Z" } wheels = [ @@ -3911,9 +3913,9 @@ name = "pyobjc-framework-securityinterface" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-security" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118, upload-time = "2025-01-14T19:05:28.569Z" } wheels = [ @@ -3926,9 +3928,9 @@ name = "pyobjc-framework-sensitivecontentanalysis" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060, upload-time = "2025-01-14T19:05:29.655Z" } wheels = [ @@ -3941,8 +3943,8 @@ name = "pyobjc-framework-servicemanagement" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882, upload-time = "2025-01-14T19:05:30.537Z" } wheels = [ @@ -3955,8 +3957,8 @@ name = "pyobjc-framework-sharedwithyou" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-sharedwithyoucore" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-sharedwithyoucore", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696, upload-time = "2025-01-14T19:05:31.396Z" } wheels = [ @@ -3969,8 +3971,8 @@ name = "pyobjc-framework-sharedwithyoucore" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877, upload-time = "2025-01-14T19:05:32.283Z" } wheels = [ @@ -3983,8 +3985,8 @@ name = "pyobjc-framework-shazamkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172, upload-time = "2025-01-14T19:05:34.497Z" } wheels = [ @@ -3997,8 +3999,8 @@ name = "pyobjc-framework-social" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806, upload-time = "2025-01-14T19:05:35.394Z" } wheels = [ @@ -4011,8 +4013,8 @@ name = "pyobjc-framework-soundanalysis" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789, upload-time = "2025-01-14T19:05:36.576Z" } wheels = [ @@ -4025,8 +4027,8 @@ name = "pyobjc-framework-speech" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620, upload-time = "2025-01-14T19:05:38.391Z" } wheels = [ @@ -4039,9 +4041,9 @@ name = "pyobjc-framework-spritekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851, upload-time = "2025-01-14T19:05:39.709Z" } wheels = [ @@ -4054,8 +4056,8 @@ name = "pyobjc-framework-storekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855, upload-time = "2025-01-14T19:05:41.605Z" } wheels = [ @@ -4068,8 +4070,8 @@ name = "pyobjc-framework-symbols" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682, upload-time = "2025-01-14T19:05:45.727Z" } wheels = [ @@ -4082,9 +4084,9 @@ name = "pyobjc-framework-syncservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coredata" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453, upload-time = "2025-01-14T19:05:46.559Z" } wheels = [ @@ -4097,8 +4099,8 @@ name = "pyobjc-framework-systemconfiguration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949, upload-time = "2025-01-14T19:05:47.466Z" } wheels = [ @@ -4111,8 +4113,8 @@ name = "pyobjc-framework-systemextensions" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531, upload-time = "2025-01-14T19:05:48.463Z" } wheels = [ @@ -4125,8 +4127,8 @@ name = "pyobjc-framework-threadnetwork" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820, upload-time = "2025-01-14T19:05:49.307Z" } wheels = [ @@ -4139,8 +4141,8 @@ name = "pyobjc-framework-uniformtypeidentifiers" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860, upload-time = "2025-01-14T19:05:50.073Z" } wheels = [ @@ -4153,8 +4155,8 @@ name = "pyobjc-framework-usernotifications" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857, upload-time = "2025-01-14T19:05:52.42Z" } wheels = [ @@ -4167,9 +4169,9 @@ name = "pyobjc-framework-usernotificationsui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-usernotifications" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-usernotifications", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369, upload-time = "2025-01-14T19:05:54.498Z" } wheels = [ @@ -4182,8 +4184,8 @@ name = "pyobjc-framework-videosubscriberaccount" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100, upload-time = "2025-01-14T19:05:55.319Z" } wheels = [ @@ -4196,10 +4198,10 @@ name = "pyobjc-framework-videotoolbox" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coremedia" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157, upload-time = "2025-01-14T19:05:56.135Z" } wheels = [ @@ -4212,8 +4214,8 @@ name = "pyobjc-framework-virtualization" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144, upload-time = "2025-01-14T19:05:57.086Z" } wheels = [ @@ -4226,10 +4228,10 @@ name = "pyobjc-framework-vision" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coreml" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreml", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175, upload-time = "2025-01-14T19:05:58.013Z" } wheels = [ @@ -4242,8 +4244,8 @@ name = "pyobjc-framework-webkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210, upload-time = "2025-01-14T19:05:59.3Z" } wheels = [ @@ -4256,9 +4258,9 @@ name = "pyopencl" version = "2025.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, - { name = "platformdirs" }, - { name = "pytools" }, + { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "pytools", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ @@ -4474,7 +4476,7 @@ name = "python-xlib" version = "0.33" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six" }, + { name = "six", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload-time = "2022-12-25T18:53:00.824Z" } wheels = [ @@ -4492,9 +4494,9 @@ name = "pytools" version = "2024.1.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs" }, - { name = "siphash24" }, - { name = "typing-extensions" }, + { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "siphash24", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ @@ -4826,7 +4828,7 @@ name = "shapely" version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload-time = "2025-04-03T09:15:05.725Z" } wheels = [ @@ -5050,12 +5052,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, ] +[[package]] +name = "xattr" +version = "1.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/bf/8b98081f9f8fd56d67b9478ff1e0f8c337cde08bcb92f0d592f0a7958983/xattr-1.1.4.tar.gz", hash = "sha256:b7b02ecb2270da5b7e7deaeea8f8b528c17368401c2b9d5f63e91f545b45d372", size = 16729, upload-time = "2025-01-06T19:19:32.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/5b/f64ba0f93e6447e1997068959f22ff99e08d77dd88d9edcf97ddcb9e9016/xattr-1.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bb4bbe37ba95542081890dd34fa5347bef4651e276647adaa802d5d0d7d86452", size = 23920, upload-time = "2025-01-06T19:17:48.234Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/ad66655f0b1317b0a297aa2d6ed7d6e5d5343495841fad535bee37a56471/xattr-1.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3da489ecef798705f9a39ea8cea4ead0d1eeed55f92c345add89740bd930bab6", size = 18883, upload-time = "2025-01-06T19:17:49.46Z" }, + { url = "https://files.pythonhosted.org/packages/4d/5d/7d5154570bbcb898e6123c292f697c87c33e12258a1a8b9741539f952681/xattr-1.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:798dd0cbe696635a6f74b06fc430818bf9c3b24314e1502eadf67027ab60c9b0", size = 19221, upload-time = "2025-01-06T19:17:51.654Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b7/135cf3018278051f57bb5dde944cb1ca4f7ad4ec383465a08c6a5c7f7152/xattr-1.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2b6361626efad5eb5a6bf8172c6c67339e09397ee8140ec41258737bea9681", size = 39098, upload-time = "2025-01-06T19:17:53.099Z" }, + { url = "https://files.pythonhosted.org/packages/a5/62/577e2eb0108158b78cd93ea3782c7a8d464693f1338a5350a1db16f69a89/xattr-1.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7fa20a0c9ce022d19123b1c5b848d00a68b837251835a7929fe041ee81dcd0", size = 36982, upload-time = "2025-01-06T19:17:54.493Z" }, + { url = "https://files.pythonhosted.org/packages/59/cc/ab3bd7a4bedf445be4b35de4a4627ef2944786724d18eaf28d05c1238c7c/xattr-1.1.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e20eeb08e2c57fc7e71f050b1cfae35cbb46105449853a582bf53fd23c5379e", size = 38891, upload-time = "2025-01-06T19:17:55.853Z" }, + { url = "https://files.pythonhosted.org/packages/45/e8/2285651d92f1460159753fe6628af259c943fcc5071e48a0540fa11dc34d/xattr-1.1.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:477370e75821bded901487e5e752cffe554d1bd3bd4839b627d4d1ee8c95a093", size = 38362, upload-time = "2025-01-06T19:17:57.078Z" }, + { url = "https://files.pythonhosted.org/packages/5f/af/7856c0b1970272a53a428bb20dc125f9fd350fb1b40ebca4e54610af1b79/xattr-1.1.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a8682091cd34a9f4a93c8aaea4101aae99f1506e24da00a3cc3dd2eca9566f21", size = 36724, upload-time = "2025-01-06T19:17:58.534Z" }, + { url = "https://files.pythonhosted.org/packages/5d/34/087e02b32d6288a40b7f6573e97a119016e6c3713d4f4b866bbf56cfb803/xattr-1.1.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2e079b3b1a274ba2121cf0da38bbe5c8d2fb1cc49ecbceb395ce20eb7d69556d", size = 37945, upload-time = "2025-01-06T19:17:59.764Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2a/d0f9e46de4cec5e4aa45fd939549b977c49dd68202fa844d07cb24ce5f17/xattr-1.1.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ae6579dea05bf9f335a082f711d5924a98da563cac72a2d550f5b940c401c0e9", size = 23917, upload-time = "2025-01-06T19:18:00.868Z" }, + { url = "https://files.pythonhosted.org/packages/83/e0/a5764257cd9c9eb598f4648a3658d915dd3520ec111ecbd251b685de6546/xattr-1.1.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd6038ec9df2e67af23c212693751481d5f7e858156924f14340376c48ed9ac7", size = 18891, upload-time = "2025-01-06T19:18:02.029Z" }, + { url = "https://files.pythonhosted.org/packages/8b/83/a81a147987387fd2841a28f767efedb099cf90e23553ead458f2330e47c5/xattr-1.1.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:608b2877526674eb15df4150ef4b70b7b292ae00e65aecaae2f192af224be200", size = 19213, upload-time = "2025-01-06T19:18:03.303Z" }, + { url = "https://files.pythonhosted.org/packages/4b/52/bf093b4eb9873ffc9e9373dcb38ec8a9b5cd4e6a9f681c4c5cf6bf067a42/xattr-1.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54dad1a6a998c6a23edfd25e99f4d38e9b942d54e518570044edf8c767687ea", size = 39302, upload-time = "2025-01-06T19:18:05.846Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d8/9d7315ebae76a7f48bc5e1aecc7e592eb43376a0f6cf470a854d895d2093/xattr-1.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0dab6ff72bb2b508f3850c368f8e53bd706585012676e1f71debba3310acde8", size = 37224, upload-time = "2025-01-06T19:18:07.226Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b2/10eb17bea7e378b2bcd76fc8c2e5158318e2c08e774b13f548f333d7318a/xattr-1.1.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a3c54c6af7cf09432b2c461af257d5f4b1cb2d59eee045f91bacef44421a46d", size = 39145, upload-time = "2025-01-06T19:18:08.403Z" }, + { url = "https://files.pythonhosted.org/packages/74/fb/95bbc28116b3c19a21acc34ec0a5973e9cc97fe49d3f47a65775af3760a8/xattr-1.1.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e346e05a158d554639fbf7a0db169dc693c2d2260c7acb3239448f1ff4a9d67f", size = 38469, upload-time = "2025-01-06T19:18:09.602Z" }, + { url = "https://files.pythonhosted.org/packages/af/03/23db582cb271ed47f2d62956e112501d998b5493f892a77104b5795ae2fc/xattr-1.1.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3ff6d9e2103d0d6e5fcd65b85a2005b66ea81c0720a37036445faadc5bbfa424", size = 36797, upload-time = "2025-01-06T19:18:10.709Z" }, + { url = "https://files.pythonhosted.org/packages/90/c4/b631d0174e097cf8c44d4f70c66545d91dc8ba15bbfa5054dd7da8371461/xattr-1.1.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7a2ee4563c6414dfec0d1ac610f59d39d5220531ae06373eeb1a06ee37cd193f", size = 38128, upload-time = "2025-01-06T19:18:11.884Z" }, +] + [[package]] name = "yapf" version = "0.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs" }, + { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ From b920e2a998eeb13e77fa383a53961b51a6247698 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 6 May 2025 07:48:15 +0800 Subject: [PATCH 115/477] tools: add --auto flag to replay and cabana for loading routes from auto source (#34863) * add flag to auto load a route from the most suitable source * split to functions * early return * add --auto to replay * README * cleanup * remove prefix * parse datetime * cleanup * improve help * do not modify logreader.py * fix seg_num * cleanup --- tools/auto_source.py | 17 ++++++++ tools/cabana/README.md | 2 + tools/cabana/cabana.cc | 4 +- tools/cabana/streams/replaystream.cc | 5 ++- tools/cabana/streams/replaystream.h | 2 +- tools/replay/README.md | 2 + tools/replay/main.cc | 14 ++++--- tools/replay/replay.cc | 4 +- tools/replay/replay.h | 2 +- tools/replay/route.cc | 58 +++++++++++++++++++++------- tools/replay/route.h | 6 ++- tools/replay/seg_mgr.h | 4 +- 12 files changed, 91 insertions(+), 29 deletions(-) create mode 100755 tools/auto_source.py diff --git a/tools/auto_source.py b/tools/auto_source.py new file mode 100755 index 0000000000..401929a9ad --- /dev/null +++ b/tools/auto_source.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +import sys +from openpilot.tools.lib.logreader import LogReader + + +def main(): + if len(sys.argv) != 2: + print("Usage: python auto_source.py ") + sys.exit(1) + + log_path = sys.argv[1] + lr = LogReader(log_path, sort_by_time=True) + print("\n".join(lr.logreader_identifiers)) + + +if __name__ == "__main__": + main() diff --git a/tools/cabana/README.md b/tools/cabana/README.md index 0b7c5bf3ee..7933098e34 100644 --- a/tools/cabana/README.md +++ b/tools/cabana/README.md @@ -12,6 +12,8 @@ Options: -h, --help Displays help on commandline options. --help-all Displays help including Qt specific options. --demo use a demo route instead of providing your own + --auto Auto load the route from the best available source (no video): + internal, openpilotci, comma_api, car_segments, testing_closet --qcam load qcamera --ecam load wide road camera --msgq read can messages from msgq diff --git a/tools/cabana/cabana.cc b/tools/cabana/cabana.cc index d140a323e1..97f178b1f3 100644 --- a/tools/cabana/cabana.cc +++ b/tools/cabana/cabana.cc @@ -23,6 +23,7 @@ int main(int argc, char *argv[]) { cmd_parser.addHelpOption(); cmd_parser.addPositionalArgument("route", "the drive to replay. find your drives at connect.comma.ai"); cmd_parser.addOption({"demo", "use a demo route instead of providing your own"}); + cmd_parser.addOption({"auto", "Auto load the route from the best available source (no video): internal, openpilotci, comma_api, car_segments, testing_closet"}); cmd_parser.addOption({"qcam", "load qcamera"}); cmd_parser.addOption({"ecam", "load wide road camera"}); cmd_parser.addOption({"dcam", "load driver camera"}); @@ -69,7 +70,8 @@ int main(int argc, char *argv[]) { } if (!route.isEmpty()) { auto replay_stream = std::make_unique(&app); - if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { + bool auto_source = cmd_parser.isSet("auto"); + if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags, auto_source)) { return 0; } stream = replay_stream.release(); diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index ebe478ded7..b8cf1be299 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -7,6 +7,7 @@ #include #include "common/timing.h" +#include "common/util.h" #include "tools/cabana/streams/routes.h" ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent) { @@ -45,9 +46,9 @@ void ReplayStream::mergeSegments() { } } -bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { +bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags, bool auto_source) { replay.reset(new Replay(route.toStdString(), {"can", "roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx", "carParams"}, - {}, nullptr, replay_flags, data_dir.toStdString())); + {}, nullptr, replay_flags, data_dir.toStdString(), auto_source)); replay->setSegmentCacheLimit(settings.max_cached_minutes); replay->installEventFilter([this](const Event *event) { return eventFilter(event); }); diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index df1f2526a5..d429ed1f95 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -18,7 +18,7 @@ class ReplayStream : public AbstractStream { public: ReplayStream(QObject *parent); void start() override { replay->start(); } - bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE); + bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE, bool auto_source = false); bool eventFilter(const Event *event); void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); } bool liveStreaming() const override { return false; } diff --git a/tools/replay/README.md b/tools/replay/README.md index 719f7d2db9..8b4afb0acc 100644 --- a/tools/replay/README.md +++ b/tools/replay/README.md @@ -64,6 +64,8 @@ Options: -s, --start start from -x playback . between 0.2 - 3 --demo use a demo route instead of providing your own + --auto Auto load the route from the best available source (no video): + internal, openpilotci, comma_api, car_segments, testing_closet --data_dir local directory with routes --prefix set OPENPILOT_PREFIX --dcam load driver camera diff --git a/tools/replay/main.cc b/tools/replay/main.cc index b33b7fa263..38a1da292a 100644 --- a/tools/replay/main.cc +++ b/tools/replay/main.cc @@ -19,6 +19,8 @@ Options: -s, --start Start from -x, --playback Playback --demo Use a demo route instead of providing your own + --auto Auto load the route from the best available source (no video): + internal, openpilotci, comma_api, car_segments, testing_closet -d, --data_dir Local directory with routes -p, --prefix Set OPENPILOT_PREFIX --dcam Load driver camera @@ -39,6 +41,7 @@ struct ReplayConfig { std::string data_dir; std::string prefix; uint32_t flags = REPLAY_FLAG_NONE; + bool auto_source = false; int start_seconds = 0; int cache_segments = -1; float playback_speed = -1; @@ -52,6 +55,7 @@ bool parseArgs(int argc, char *argv[], ReplayConfig &config) { {"start", required_argument, nullptr, 's'}, {"playback", required_argument, nullptr, 'x'}, {"demo", no_argument, nullptr, 0}, + {"auto", no_argument, nullptr, 0}, {"data_dir", required_argument, nullptr, 'd'}, {"prefix", required_argument, nullptr, 'p'}, {"dcam", no_argument, nullptr, 0}, @@ -94,11 +98,9 @@ bool parseArgs(int argc, char *argv[], ReplayConfig &config) { case 'p': config.prefix = optarg; break; case 0: { std::string name = cli_options[option_index].name; - if (name == "demo") { - config.route = DEMO_ROUTE; - } else { - config.flags |= flag_map.at(name); - } + if (name == "demo") config.route = DEMO_ROUTE; + else if (name == "auto") config.auto_source = true; + else config.flags |= flag_map.at(name); break; } case 'h': std::cout << helpText; return false; @@ -136,7 +138,7 @@ int main(int argc, char *argv[]) { op_prefix = std::make_unique(config.prefix); } - Replay replay(config.route, config.allow, config.block, nullptr, config.flags, config.data_dir); + Replay replay(config.route, config.allow, config.block, nullptr, config.flags, config.data_dir, config.auto_source); if (config.cache_segments > 0) { replay.setSegmentCacheLimit(config.cache_segments); } diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index 80f586daa6..a8e5cd9d43 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -15,8 +15,8 @@ void notifyEvent(Callback &callback, Args &&...args) { } Replay::Replay(const std::string &route, std::vector allow, std::vector block, - SubMaster *sm, uint32_t flags, const std::string &data_dir) - : sm_(sm), flags_(flags), seg_mgr_(std::make_unique(route, flags, data_dir)) { + SubMaster *sm, uint32_t flags, const std::string &data_dir, bool auto_source) + : sm_(sm), flags_(flags), seg_mgr_(std::make_unique(route, flags, data_dir, auto_source)) { std::signal(SIGUSR1, interrupt_sleep_handler); if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) { diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 6a2c86ff02..5e868d2427 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -29,7 +29,7 @@ enum REPLAY_FLAGS { class Replay { public: Replay(const std::string &route, std::vector allow, std::vector block, SubMaster *sm = nullptr, - uint32_t flags = REPLAY_FLAG_NONE, const std::string &data_dir = ""); + uint32_t flags = REPLAY_FLAG_NONE, const std::string &data_dir = "", bool auto_source = false); ~Replay(); bool load(); RouteLoadError lastRouteError() const { return route().lastError(); } diff --git a/tools/replay/route.cc b/tools/replay/route.cc index 98fa0e290e..ba00828267 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -10,9 +10,8 @@ #include "tools/replay/replay.h" #include "tools/replay/util.h" -Route::Route(const std::string &route, const std::string &data_dir) : data_dir_(data_dir) { - route_ = parseRoute(route); -} +Route::Route(const std::string &route, const std::string &data_dir, bool auto_source) + : route_string_(route), data_dir_(data_dir), auto_source_(auto_source) {} RouteIdentifier Route::parseRoute(const std::string &str) { RouteIdentifier identifier = {}; @@ -44,7 +43,7 @@ RouteIdentifier Route::parseRoute(const std::string &str) { } bool Route::load() { - err_ = RouteLoadError::None; + route_ = parseRoute(route_string_); if (route_.str.empty() || (data_dir_.empty() && route_.dongle_id.empty())) { rInfo("invalid route format"); return false; @@ -56,17 +55,50 @@ bool Route::load() { date_time_ = mktime(&tm_time); } - bool ret = data_dir_.empty() ? loadFromServer() : loadFromLocal(); - if (ret) { - if (route_.begin_segment == -1) route_.begin_segment = segments_.rbegin()->first; - if (route_.end_segment == -1) route_.end_segment = segments_.rbegin()->first; - for (auto it = segments_.begin(); it != segments_.end(); /**/) { - if (it->first < route_.begin_segment || it->first > route_.end_segment) { - it = segments_.erase(it); - } else { - ++it; + if (!loadSegments()) { + rInfo("Failed to load segments"); + return false; + } + + return true; +} + +bool Route::loadSegments() { + if (!auto_source_) { + bool ret = data_dir_.empty() ? loadFromServer() : loadFromLocal(); + if (ret) { + // Trim segments + if (route_.begin_segment > 0) { + segments_.erase(segments_.begin(), segments_.lower_bound(route_.begin_segment)); + } + if (route_.end_segment >= 0) { + segments_.erase(segments_.upper_bound(route_.end_segment), segments_.end()); } } + return !segments_.empty(); + } + return loadFromAutoSource(); +} + +bool Route::loadFromAutoSource() { + auto origin_prefix = getenv("OPENPILOT_PREFIX"); + if (origin_prefix) { + setenv("OPENPILOT_PREFIX", "", 1); + } + auto cmd = util::string_format("../auto_source.py \"%s\"", route_string_.c_str()); + auto log_files = split(util::check_output(cmd), '\n'); + if (origin_prefix) { + setenv("OPENPILOT_PREFIX", origin_prefix, 1); + } + + const static std::regex rx(R"(\/(\d+)\/)"); + for (int i = 0; i < log_files.size(); ++i) { + int seg_num = i; + std::smatch match; + if (std::regex_search(log_files[i], match, rx)) { + seg_num = std::stoi(match[1]); + } + addFileToSegment(seg_num, log_files[i]); } return !segments_.empty(); } diff --git a/tools/replay/route.h b/tools/replay/route.h index fb9f1869f6..0375252a19 100644 --- a/tools/replay/route.h +++ b/tools/replay/route.h @@ -40,7 +40,7 @@ struct SegmentFile { class Route { public: - Route(const std::string &route, const std::string &data_dir = {}); + Route(const std::string &route, const std::string &data_dir = {}, bool auto_source = false); bool load(); RouteLoadError lastError() const { return err_; } inline const std::string &name() const { return route_.str; } @@ -52,6 +52,8 @@ public: static RouteIdentifier parseRoute(const std::string &str); protected: + bool loadSegments(); + bool loadFromAutoSource(); bool loadFromLocal(); bool loadFromServer(int retries = 3); bool loadFromJson(const std::string &json); @@ -61,6 +63,8 @@ protected: std::map segments_; std::time_t date_time_ = 0; RouteLoadError err_ = RouteLoadError::None; + bool auto_source_ = false; + std::string route_string_; }; class Segment { diff --git a/tools/replay/seg_mgr.h b/tools/replay/seg_mgr.h index 9158e41618..640169749e 100644 --- a/tools/replay/seg_mgr.h +++ b/tools/replay/seg_mgr.h @@ -20,8 +20,8 @@ public: bool isSegmentLoaded(int n) const { return segments.find(n) != segments.end(); } }; - SegmentManager(const std::string &route_name, uint32_t flags, const std::string &data_dir = "") - : flags_(flags), route_(route_name, data_dir), event_data_(std::make_shared()) {} + SegmentManager(const std::string &route_name, uint32_t flags, const std::string &data_dir = "", bool auto_source = false) + : flags_(flags), route_(route_name, data_dir, auto_source), event_data_(std::make_shared()) {} ~SegmentManager(); bool load(); From 3c58da5c84064191ced8ece01ffd0b7a0393440f Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Mon, 5 May 2025 16:54:14 -0700 Subject: [PATCH 116/477] mimic agnos pyproject order --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6db5f00621..bf43840d71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ dependencies = [ # logging "pyzmq", "sentry-sdk", + "xattr", # used in place of 'os.getxattr' for macos compatibility # athena "PyJWT", @@ -64,7 +65,6 @@ dependencies = [ "psutil", "pycryptodome", # used in updated/casync, panda, body, and a test "setproctitle", - "xattr", # used in place of 'os.getxattr' for macos compatibility # logreader "zstandard", From 5c1f28591f8ff2e11b58ad8c99a4a450545a8783 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 5 May 2025 17:07:27 -0700 Subject: [PATCH 117/477] remove dead link in SAFETY.md (#35122) * Update SAFETY.md remove broken link * Update SAFETY.md * Update docs/SAFETY.md --------- Co-authored-by: Shane Smiskol --- docs/SAFETY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/SAFETY.md b/docs/SAFETY.md index 4b568728a7..18a450a395 100644 --- a/docs/SAFETY.md +++ b/docs/SAFETY.md @@ -25,9 +25,9 @@ ensuring two main safety requirements. by stepping on the brake pedal or by pressing the cancel button. 2. The vehicle must not alter its trajectory too quickly for the driver to safely react. This means that while the system is engaged, the actuators are constrained - to operate within reasonable limits[^1]. + to operate within reasonable limits[^1]. -For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [panda/board/safety/](https://github.com/commaai/panda/tree/master/board/safety). +For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [opendbc/safety/safety](https://github.com/commaai/opendbc/tree/master/opendbc/safety/safety). **Extra note**: comma.ai strongly discourages the use of openpilot forks with safety code either missing or not fully meeting the above requirements. From ddb19cc0742a8f7cbf6e71fdd00dfbb5c7a80139 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 6 May 2025 10:21:23 -0700 Subject: [PATCH 118/477] op: better adb SSH (#35135) * op: better adb SSH * fix --- tools/adb_shell.sh | 8 -------- tools/op.sh | 2 +- tools/scripts/adb_ssh.sh | 7 +++++++ 3 files changed, 8 insertions(+), 9 deletions(-) delete mode 100755 tools/adb_shell.sh create mode 100755 tools/scripts/adb_ssh.sh diff --git a/tools/adb_shell.sh b/tools/adb_shell.sh deleted file mode 100755 index f757f7d4d0..0000000000 --- a/tools/adb_shell.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env expect -spawn adb shell -expect "#" -send "cd data/openpilot\r" -send "export TERM=xterm-256color\r" -send "su comma\r" -send "clear\r" -interact diff --git a/tools/op.sh b/tools/op.sh index 8a8faaf237..25a4670cd1 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -275,7 +275,7 @@ function op_venv() { function op_adb() { op_before_cmd - op_run_command tools/adb_shell.sh + op_run_command tools/scripts/adb_ssh.sh } function op_check() { diff --git a/tools/scripts/adb_ssh.sh b/tools/scripts/adb_ssh.sh new file mode 100755 index 0000000000..43c8e07de6 --- /dev/null +++ b/tools/scripts/adb_ssh.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -e + +# this is a little nicer than "adb shell" since +# "adb shell" doesn't do full terminal emulation +adb forward tcp:2222 tcp:22 +ssh comma@localhost -p 2222 From 9622b6f8bd434f21ec85a5034e062218a57f9659 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 7 May 2025 01:25:51 +0800 Subject: [PATCH 119/477] ui(raylib): add a simple toggle component (#35128) * simple toggle * Update system/ui/lib/toggle.py Co-authored-by: Adeeb Shihadeh * cleanup --------- Co-authored-by: Adeeb Shihadeh --- system/ui/lib/toggle.py | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 system/ui/lib/toggle.py diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py new file mode 100644 index 0000000000..e72faef11c --- /dev/null +++ b/system/ui/lib/toggle.py @@ -0,0 +1,53 @@ +import pyray as rl + +ON_COLOR = rl.GREEN +OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) +KNOB_COLOR = rl.WHITE +BG_HEIGHT = 60 +KNOB_HEIGHT = 80 +WIDTH = 160 + + +class Toggle: + def __init__(self, x, y, initial_state=False): + self._state = initial_state + self._rect = rl.Rectangle(x, y, WIDTH, KNOB_HEIGHT) + + def handle_input(self): + if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON): + mouse_pos = rl.get_mouse_position() + if rl.check_collision_point_rec(mouse_pos, self._rect): + self._state = not self._state + + def get_state(self): + return self._state + + def render(self): + self._draw_background() + self._draw_knob() + + def _draw_background(self): + bg_rect = rl.Rectangle( + self._rect.x + 5, + self._rect.y + (KNOB_HEIGHT - BG_HEIGHT) / 2, + self._rect.width - 10, + BG_HEIGHT, + ) + rl.draw_rectangle_rounded(bg_rect, 1.0, 10, ON_COLOR if self._state else OFF_COLOR) + + def _draw_knob(self): + knob_radius = KNOB_HEIGHT / 2 + knob_x = self._rect.x + knob_radius if not self._state else self._rect.x + self._rect.width - knob_radius + knob_y = self._rect.y + knob_radius + rl.draw_circle(int(knob_x), int(knob_y), knob_radius, KNOB_COLOR) + + +if __name__ == "__main__": + from openpilot.system.ui.lib.application import gui_app + + gui_app.init_window("Text toggle example") + toggle = Toggle(100, 100) + for _ in gui_app.render(): + toggle.handle_input() + toggle.render() + From 662877c6f3585493d0fa1f378ab402afd3353bc1 Mon Sep 17 00:00:00 2001 From: Nayan Date: Tue, 6 May 2025 13:38:06 -0400 Subject: [PATCH 120/477] models: Consolidate model helpers & get_model_path filename fix (#884) consolidate run_helpers.py & helpers.py bugfix for get_model_path --- sunnypilot/modeld/modeld.py | 2 +- sunnypilot/modeld/runners/run_helpers.py | 97 ------------------------ sunnypilot/models/helpers.py | 88 ++++++++++++++++++++- 3 files changed, 88 insertions(+), 99 deletions(-) delete mode 100644 sunnypilot/modeld/runners/run_helpers.py diff --git a/sunnypilot/modeld/modeld.py b/sunnypilot/modeld/modeld.py index a58a567100..744b90465b 100755 --- a/sunnypilot/modeld/modeld.py +++ b/sunnypilot/modeld/modeld.py @@ -23,7 +23,7 @@ from openpilot.sunnypilot.modeld.parse_model_outputs import Parser from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.sunnypilot.modeld.constants import ModelConstants from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext -from openpilot.sunnypilot.modeld.runners.run_helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants +from openpilot.sunnypilot.models.helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants PROCESS_NAME = "selfdrive.modeld.modeld_snpe" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') diff --git a/sunnypilot/modeld/runners/run_helpers.py b/sunnypilot/modeld/runners/run_helpers.py deleted file mode 100644 index 3e6b280ae1..0000000000 --- a/sunnypilot/modeld/runners/run_helpers.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - -This file is part of sunnypilot and is licensed under the MIT License. -See the LICENSE.md file in the root directory for more details. -""" - -import os -import pickle -import numpy as np - -from cereal import custom -from openpilot.sunnypilot.modeld.constants import Meta, MetaTombRaider, MetaSimPose -from openpilot.sunnypilot.modeld.runners import ModelRunner -from openpilot.sunnypilot.models.helpers import get_active_bundle -from openpilot.system.hardware import PC -from openpilot.system.hardware.hw import Paths -from pathlib import Path - -USE_ONNX = os.getenv('USE_ONNX', PC) - -CUSTOM_MODEL_PATH = Paths.model_root() -METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl' - -ModelManager = custom.ModelManagerSP - - -def _get_model(): - if bundle := get_active_bundle(): - drive_model = next(model for model in bundle.models if model.type == ModelManager.Model.Type.supercombo) - return drive_model - - return None - -def get_model_path(): - if USE_ONNX: - return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'} - - if model := _get_model(): - return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{model.fileName}"} - - return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'} - - -def load_metadata(): - metadata_path = METADATA_PATH - - if model := _get_model(): - metadata_path = f"{CUSTOM_MODEL_PATH}/{model.metadata.fileName}" - - with open(metadata_path, 'rb') as f: - return pickle.load(f) - - -def prepare_inputs(model_metadata) -> dict[str, np.ndarray]: - # img buffers are managed in openCL transform code so we don't pass them as inputs - inputs = { - k: np.zeros(v, dtype=np.float32).flatten() - for k, v in model_metadata['input_shapes'].items() - if 'img' not in k - } - - return inputs - - -def load_meta_constants(model_metadata): - """ - Determines and loads the appropriate meta model class based on the metadata provided. The function checks - specific keys and conditions within the provided metadata dictionary to identify the corresponding meta - model class to return. - - :param model_metadata: Dictionary containing metadata about the model. It includes - details such as input shapes, output slices, and other configurations for identifying - metadata-dependent meta model classes. - :type model_metadata: dict - :return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider) - based on the conditions and metadata provided. - :rtype: type - """ - meta = Meta # Default Meta - - if 'sim_pose' in model_metadata['input_shapes'].keys(): - # Meta for models with sim_pose input - meta = MetaSimPose - else: - # Meta for Tomb Raider, it does not include sim_pose input but has the same meta slice as previous models - meta_slice = model_metadata['output_slices']['meta'] - meta_tf_slice = slice(5868, 5921, None) - - if ( - meta_slice.start == meta_tf_slice.start and - meta_slice.stop == meta_tf_slice.stop and - meta_slice.step == meta_tf_slice.step - ): - meta = MetaTombRaider - - return meta diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py index 20f58f8c59..7bcf1a148a 100644 --- a/sunnypilot/models/helpers.py +++ b/sunnypilot/models/helpers.py @@ -7,13 +7,28 @@ See the LICENSE.md file in the root directory for more details. import hashlib import os +import pickle +import numpy as np +import json + from openpilot.common.params import Params from cereal import custom -import json +from openpilot.sunnypilot.modeld.constants import Meta, MetaTombRaider, MetaSimPose +from openpilot.sunnypilot.modeld.runners import ModelRunner +from openpilot.system.hardware import PC +from openpilot.system.hardware.hw import Paths +from pathlib import Path CURRENT_SELECTOR_VERSION = 2 REQUIRED_MIN_SELECTOR_VERSION = 2 +USE_ONNX = os.getenv('USE_ONNX', PC) + +CUSTOM_MODEL_PATH = Paths.model_root() +METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl' + +ModelManager = custom.ModelManagerSP + async def verify_file(file_path: str, expected_hash: str) -> bool: """Verifies file hash against expected hash""" @@ -98,3 +113,74 @@ def get_active_model_runner(params: Params = None, force_check=False) -> custom. params.put("ModelRunnerTypeCache", str(int(runner_type))) return runner_type + +def _get_model(): + if bundle := get_active_bundle(): + drive_model = next(model for model in bundle.models if model.type == ModelManager.Model.Type.supercombo) + return drive_model + + return None + +def get_model_path(): + if USE_ONNX: + return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'} + + if model := _get_model(): + return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{model.artifact.fileName}"} + + return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'} + + +def load_metadata(): + metadata_path = METADATA_PATH + + if model := _get_model(): + metadata_path = f"{CUSTOM_MODEL_PATH}/{model.metadata.fileName}" + + with open(metadata_path, 'rb') as f: + return pickle.load(f) + + +def prepare_inputs(model_metadata) -> dict[str, np.ndarray]: + # img buffers are managed in openCL transform code so we don't pass them as inputs + inputs = { + k: np.zeros(v, dtype=np.float32).flatten() + for k, v in model_metadata['input_shapes'].items() + if 'img' not in k + } + + return inputs + + +def load_meta_constants(model_metadata): + """ + Determines and loads the appropriate meta model class based on the metadata provided. The function checks + specific keys and conditions within the provided metadata dictionary to identify the corresponding meta + model class to return. + + :param model_metadata: Dictionary containing metadata about the model. It includes + details such as input shapes, output slices, and other configurations for identifying + metadata-dependent meta model classes. + :type model_metadata: dict + :return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider) + based on the conditions and metadata provided. + :rtype: type + """ + meta = Meta # Default Meta + + if 'sim_pose' in model_metadata['input_shapes'].keys(): + # Meta for models with sim_pose input + meta = MetaSimPose + else: + # Meta for Tomb Raider, it does not include sim_pose input but has the same meta slice as previous models + meta_slice = model_metadata['output_slices']['meta'] + meta_tf_slice = slice(5868, 5921, None) + + if ( + meta_slice.start == meta_tf_slice.start and + meta_slice.stop == meta_tf_slice.stop and + meta_slice.step == meta_tf_slice.step + ): + meta = MetaTombRaider + + return meta From 58bc8e3b43884a186c2054d258b0637aafbb7c7a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 6 May 2025 10:51:15 -0700 Subject: [PATCH 121/477] op: more robust openpilot finding (#35136) --- tools/op.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/op.sh b/tools/op.sh index 25a4670cd1..93e774a2ac 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -52,6 +52,7 @@ function op_run_command() { # be default, assume openpilot dir is in current directory OPENPILOT_ROOT=$(pwd) function op_get_openpilot_dir() { + # First try traversing up the directory tree while [[ "$OPENPILOT_ROOT" != '/' ]]; do if find "$OPENPILOT_ROOT/launch_openpilot.sh" -maxdepth 1 -mindepth 1 &> /dev/null; then @@ -59,6 +60,14 @@ function op_get_openpilot_dir() { fi OPENPILOT_ROOT="$(readlink -f "$OPENPILOT_ROOT/"..)" done + + # Fallback to hardcoded directories if not found + for dir in "$HOME/openpilot" "/data/openpilot"; do + if [[ -f "$dir/launch_openpilot.sh" ]]; then + OPENPILOT_ROOT="$dir" + return 0 + fi + done } function op_install_post_commit() { @@ -378,11 +387,6 @@ function op_default() { echo " op is only a wrapper for existing scripts, tools, and commands." echo " op will always show you what it will run on your system." echo "" - echo " op will try to find your openpilot directory in the following order:" - echo " 1: use the directory specified with the --dir option" - echo " 2: use the current working directory" - echo " 3: go up the file tree non-recursively" - echo "" echo -e "${BOLD}${UNDERLINE}Usage:${NC} op [OPTIONS] " echo "" echo -e "${BOLD}${UNDERLINE}Commands [System]:${NC}" From 97bad78553746f6c1f1c0217ebb34e8446f4171d Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 6 May 2025 14:29:16 -0700 Subject: [PATCH 122/477] bump opendbc --- RELEASES.md | 1 + opendbc_repo | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 3ff80ca4dc..5730877f94 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,7 @@ Version 0.9.9 (2025-05-15) * New training architecture supervised by MLSIM * Steering actuator delay is now learned online * Tesla Model 3 and Y support thanks to lukasloetkolben! +* Lexus RC 2023 support thanks to nelsonjchen! * Coming soon * New Honda models * Bigger vision model diff --git a/opendbc_repo b/opendbc_repo index de651d84e8..1754a76924 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit de651d84e8514e58aaa6073b06327706b93f5b8a +Subproject commit 1754a769240f170b9033d802be549bd69e16b6c0 From 2393e0d27d2bc42c9d79cfd9966bab5dd31a48d7 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 6 May 2025 14:54:24 -0700 Subject: [PATCH 123/477] update CARS doc --- docs/CARS.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index c22568ce2e..75917241d6 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,11 +4,12 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 304 Supported Cars +# 306 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -186,6 +187,7 @@ A supported vehicle is one that just works when you install a comma device. All |Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RC 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| From e64be675e32bfae07c433c7cb03ba537c810cab8 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 6 May 2025 14:57:35 -0700 Subject: [PATCH 124/477] don't need to assert, CI updates this --- selfdrive/car/tests/test_docs.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/selfdrive/car/tests/test_docs.py b/selfdrive/car/tests/test_docs.py index 43c30eb64d..6e13d55b29 100644 --- a/selfdrive/car/tests/test_docs.py +++ b/selfdrive/car/tests/test_docs.py @@ -4,7 +4,7 @@ from openpilot.common.basedir import BASEDIR from opendbc.car.docs import generate_cars_md, get_all_car_docs from openpilot.selfdrive.debug.dump_car_docs import dump_car_docs from openpilot.selfdrive.debug.print_docs_diff import print_car_docs_diff -from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE +from openpilot.selfdrive.car.docs import CARS_MD_TEMPLATE class TestCarDocs: @@ -13,11 +13,7 @@ class TestCarDocs: cls.all_cars = get_all_car_docs() def test_generator(self): - generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE) - with open(CARS_MD_OUT) as f: - current_cars_md = f.read() - - assert generated_cars_md == current_cars_md, "Run selfdrive/car/docs.py to update the compatibility documentation" + generate_cars_md(self.all_cars, CARS_MD_TEMPLATE) def test_docs_diff(self): dump_path = os.path.join(BASEDIR, "selfdrive", "car", "tests", "cars_dump") From b4003120422b586c379c12c071609d1a068cb885 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 6 May 2025 20:37:09 -0700 Subject: [PATCH 125/477] agnos 12 (#35133) * new * all * prod * both * 12 * version * update --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 24 ++++++------ system/hardware/tici/all-partitions.json | 50 ++++++++++++------------ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index aff451e541..9ce5013240 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="11.13" + export AGNOS_VERSION="12" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 1a3cd382a9..9d56c95fb4 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -56,29 +56,29 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz", - "hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", - "hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", + "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", + "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf" + "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz", - "hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824", - "hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "size": 4404019200, + "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img.xz", + "hash": "9b2b6df2ebc381404eef4a1765bf2657fb69b995d270d0c577bdaa5932e846fb", + "hash_raw": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", + "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533", + "ondevice_hash": "3ff6c83006a739c11fea2c78347f2206f6050a7587ee355f4935846fdfdb5385", "alt": { - "hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img", - "size": 4404019200 + "hash": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", + "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img", + "size": 5368709120 } } ] \ No newline at end of file diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 4e24b229d4..15d41cc8ab 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz", - "hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", - "hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", + "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", + "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf" + "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz", - "hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824", - "hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "size": 4404019200, + "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img.xz", + "hash": "9b2b6df2ebc381404eef4a1765bf2657fb69b995d270d0c577bdaa5932e846fb", + "hash_raw": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", + "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533", + "ondevice_hash": "3ff6c83006a739c11fea2c78347f2206f6050a7587ee355f4935846fdfdb5385", "alt": { - "hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img", - "size": 4404019200 + "hash": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", + "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img", + "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108.img.xz", - "hash": "ff01a0ca5a2ea6661f836248043a211cd8d71c3269c139cb574b56855fabc3f4", - "hash_raw": "175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f349c0b41915abc6526b4de7e9f2e1a4909fc3256ea91b597cc6512ffa3d31f0.img.xz", + "hash": "0510c16927f81cbfb30cb51a1adec3eeeac4c6c7fbf5ac0ba2b87909ef4d1363", + "hash_raw": "f349c0b41915abc6526b4de7e9f2e1a4909fc3256ea91b597cc6512ffa3d31f0", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "2f3d69e5015a45a18c3553f2edc5706aacd6d84a4b3d5010a3d76a1a3aa910b0" + "ondevice_hash": "5afb1b7859f210a250ea40e894aaa158dc7c72824565312a0f8381b1ec7d03c0" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b.img.xz", - "hash": "714970777e02bb53a71640735bdb84b3071ecbc0346b978ce12eb667d75634ec", - "hash_raw": "61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-19287b0f0e3efc5dce1b1b686b2be02bb9ba1d80d896bbff605671afe389651c.img.xz", + "hash": "f9e949b03e224a507bcaae2b37790b2ec9c2941afdff5ffca9f5f1aeec81713a", + "hash_raw": "19287b0f0e3efc5dce1b1b686b2be02bb9ba1d80d896bbff605671afe389651c", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "95e6889a808b8d266660990e67e917cf3b63179f23588565af7f2fa54f70ac76" + "ondevice_hash": "ae80936894bd134cc23cca12aa325f24b2a217163d34c4fab65ef8f0c5448418" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997.img.xz", - "hash": "33e5ab398620f147b885a9627b2608591bd9e1c9aa481eb705dc86707d706ea2", - "hash_raw": "a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6d0e28b0945a7b4e284c06393199692ca258383d912419012f835bb20ece3d93.img.xz", + "hash": "e25657872471bd6a7e2c71eda2306d365098b458ebd5a834d002b84c5654bb7b", + "hash_raw": "6d0e28b0945a7b4e284c06393199692ca258383d912419012f835bb20ece3d93", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "cd6291dea40968123f7af0b831cbfbbd6e515b676f2e427ae47ff358f6ac148e" + "ondevice_hash": "2d24d20e9fbdcc8b098990970738465a5f537611559d69fa81095d03ac44ac51" } -] +] \ No newline at end of file From bbf37ae5c7e95f84f7460b20ce898a5ce0e20a6a Mon Sep 17 00:00:00 2001 From: Andrei Radulescu Date: Wed, 7 May 2025 07:09:59 +0300 Subject: [PATCH 126/477] Reapply "Mypy: Got passing on macos (#34591)" (#35126) * Mypy: Got passing on macos (#34591) * Mypy: Got mypy passing on macos * common/realtime.py refactor * Mypy: mypy passing on darwin * Refactor: Removed else: pass statement * Refactor: Removed unnecessary check * added xattr to pyproject * loggerd: switched to xatter module * loggerd: removed unused module in xattr_cache.py * UV: update uv.lock * Update system/athena/athenad.py Co-authored-by: Maxime Desroches * athenad: fixed blank lines * loggerd: refactor of xattr_cache * cleanup --------- Co-authored-by: Maxime Desroches * fix getxattr no attribute on macOS * try fixing missing ENOATTR on Linux --------- Co-authored-by: BrainLess <116778989+BrainLessPea@users.noreply.github.com> Co-authored-by: Maxime Desroches --- common/realtime.py | 5 +++-- system/athena/athenad.py | 7 +++++-- system/loggerd/xattr_cache.py | 11 ++++++----- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/common/realtime.py b/common/realtime.py index 82176a00a6..57926b4c4f 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -1,6 +1,7 @@ """Utilities for reading real time clocks and keeping soft real time constraints.""" import gc import os +import sys import time from setproctitle import getproctitle @@ -28,13 +29,13 @@ class Priority: def set_core_affinity(cores: list[int]) -> None: - if not PC: + if sys.platform == 'linux' and not PC: os.sched_setaffinity(0, cores) def config_realtime_process(cores: int | list[int], priority: int) -> None: gc.disable() - if not PC: + if sys.platform == 'linux' and not PC: os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority)) c = cores if isinstance(cores, list) else [cores, ] set_core_affinity(c) diff --git a/system/athena/athenad.py b/system/athena/athenad.py index 5813bc90a5..fb546fa40f 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -776,8 +776,11 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None: # While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s # offroad, we can expect to time out in 30 + (10 * 3) = 60s # FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) + if sys.platform == 'linux': + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) + elif sys.platform == 'darwin': + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 7 if onroad else 30) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3) diff --git a/system/loggerd/xattr_cache.py b/system/loggerd/xattr_cache.py index d3220118ac..39bb172059 100644 --- a/system/loggerd/xattr_cache.py +++ b/system/loggerd/xattr_cache.py @@ -1,16 +1,17 @@ -import os import errno +import xattr + _cached_attributes: dict[tuple, bytes | None] = {} def getxattr(path: str, attr_name: str) -> bytes | None: key = (path, attr_name) if key not in _cached_attributes: try: - response = os.getxattr(path, attr_name) + response = xattr.getxattr(path, attr_name) except OSError as e: - # ENODATA means attribute hasn't been set - if e.errno == errno.ENODATA: + # ENODATA (Linux) or ENOATTR (macOS) means attribute hasn't been set + if e.errno == errno.ENODATA or (hasattr(errno, 'ENOATTR') and e.errno == errno.ENOATTR): response = None else: raise @@ -19,4 +20,4 @@ def getxattr(path: str, attr_name: str) -> bytes | None: def setxattr(path: str, attr_name: str, attr_value: bytes) -> None: _cached_attributes.pop((path, attr_name), None) - return os.setxattr(path, attr_name, attr_value) + xattr.setxattr(path, attr_name, attr_value) From aa1b7907086b5c1fe5e7b07c92e5aa6090a40c23 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 6 May 2025 21:38:50 -0700 Subject: [PATCH 127/477] revert agnos 12 --- common/realtime.py | 5 +-- launch_env.sh | 2 +- system/athena/athenad.py | 7 +--- system/hardware/tici/agnos.json | 24 ++++++------ system/hardware/tici/all-partitions.json | 50 ++++++++++++------------ system/loggerd/xattr_cache.py | 11 +++--- 6 files changed, 47 insertions(+), 52 deletions(-) diff --git a/common/realtime.py b/common/realtime.py index 57926b4c4f..82176a00a6 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -1,7 +1,6 @@ """Utilities for reading real time clocks and keeping soft real time constraints.""" import gc import os -import sys import time from setproctitle import getproctitle @@ -29,13 +28,13 @@ class Priority: def set_core_affinity(cores: list[int]) -> None: - if sys.platform == 'linux' and not PC: + if not PC: os.sched_setaffinity(0, cores) def config_realtime_process(cores: int | list[int], priority: int) -> None: gc.disable() - if sys.platform == 'linux' and not PC: + if not PC: os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority)) c = cores if isinstance(cores, list) else [cores, ] set_core_affinity(c) diff --git a/launch_env.sh b/launch_env.sh index 9ce5013240..aff451e541 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12" + export AGNOS_VERSION="11.13" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/athena/athenad.py b/system/athena/athenad.py index fb546fa40f..5813bc90a5 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -776,11 +776,8 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None: # While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s # offroad, we can expect to time out in 30 + (10 * 3) = 60s # FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused - if sys.platform == 'linux': - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) - elif sys.platform == 'darwin': - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 7 if onroad else 30) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3) diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 9d56c95fb4..1a3cd382a9 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -56,29 +56,29 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", - "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", - "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz", + "hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", + "hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" + "ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img.xz", - "hash": "9b2b6df2ebc381404eef4a1765bf2657fb69b995d270d0c577bdaa5932e846fb", - "hash_raw": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", - "size": 5368709120, + "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz", + "hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824", + "hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", + "size": 4404019200, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "3ff6c83006a739c11fea2c78347f2206f6050a7587ee355f4935846fdfdb5385", + "ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533", "alt": { - "hash": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", - "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img", - "size": 5368709120 + "hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", + "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img", + "size": 4404019200 } } ] \ No newline at end of file diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 15d41cc8ab..4e24b229d4 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", - "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", - "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz", + "hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", + "hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" + "ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img.xz", - "hash": "9b2b6df2ebc381404eef4a1765bf2657fb69b995d270d0c577bdaa5932e846fb", - "hash_raw": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", - "size": 5368709120, + "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz", + "hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824", + "hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", + "size": 4404019200, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "3ff6c83006a739c11fea2c78347f2206f6050a7587ee355f4935846fdfdb5385", + "ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533", "alt": { - "hash": "95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5", - "url": "https://commadist.azureedge.net/agnosupdate/system-95177f5f90649586750bbea7d1b8f5e7db694f95966ac041e43716469011d1f5.img", - "size": 5368709120 + "hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", + "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img", + "size": 4404019200 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f349c0b41915abc6526b4de7e9f2e1a4909fc3256ea91b597cc6512ffa3d31f0.img.xz", - "hash": "0510c16927f81cbfb30cb51a1adec3eeeac4c6c7fbf5ac0ba2b87909ef4d1363", - "hash_raw": "f349c0b41915abc6526b4de7e9f2e1a4909fc3256ea91b597cc6512ffa3d31f0", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108.img.xz", + "hash": "ff01a0ca5a2ea6661f836248043a211cd8d71c3269c139cb574b56855fabc3f4", + "hash_raw": "175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "5afb1b7859f210a250ea40e894aaa158dc7c72824565312a0f8381b1ec7d03c0" + "ondevice_hash": "2f3d69e5015a45a18c3553f2edc5706aacd6d84a4b3d5010a3d76a1a3aa910b0" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-19287b0f0e3efc5dce1b1b686b2be02bb9ba1d80d896bbff605671afe389651c.img.xz", - "hash": "f9e949b03e224a507bcaae2b37790b2ec9c2941afdff5ffca9f5f1aeec81713a", - "hash_raw": "19287b0f0e3efc5dce1b1b686b2be02bb9ba1d80d896bbff605671afe389651c", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b.img.xz", + "hash": "714970777e02bb53a71640735bdb84b3071ecbc0346b978ce12eb667d75634ec", + "hash_raw": "61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "ae80936894bd134cc23cca12aa325f24b2a217163d34c4fab65ef8f0c5448418" + "ondevice_hash": "95e6889a808b8d266660990e67e917cf3b63179f23588565af7f2fa54f70ac76" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-6d0e28b0945a7b4e284c06393199692ca258383d912419012f835bb20ece3d93.img.xz", - "hash": "e25657872471bd6a7e2c71eda2306d365098b458ebd5a834d002b84c5654bb7b", - "hash_raw": "6d0e28b0945a7b4e284c06393199692ca258383d912419012f835bb20ece3d93", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997.img.xz", + "hash": "33e5ab398620f147b885a9627b2608591bd9e1c9aa481eb705dc86707d706ea2", + "hash_raw": "a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "2d24d20e9fbdcc8b098990970738465a5f537611559d69fa81095d03ac44ac51" + "ondevice_hash": "cd6291dea40968123f7af0b831cbfbbd6e515b676f2e427ae47ff358f6ac148e" } -] \ No newline at end of file +] diff --git a/system/loggerd/xattr_cache.py b/system/loggerd/xattr_cache.py index 39bb172059..d3220118ac 100644 --- a/system/loggerd/xattr_cache.py +++ b/system/loggerd/xattr_cache.py @@ -1,17 +1,16 @@ +import os import errno -import xattr - _cached_attributes: dict[tuple, bytes | None] = {} def getxattr(path: str, attr_name: str) -> bytes | None: key = (path, attr_name) if key not in _cached_attributes: try: - response = xattr.getxattr(path, attr_name) + response = os.getxattr(path, attr_name) except OSError as e: - # ENODATA (Linux) or ENOATTR (macOS) means attribute hasn't been set - if e.errno == errno.ENODATA or (hasattr(errno, 'ENOATTR') and e.errno == errno.ENOATTR): + # ENODATA means attribute hasn't been set + if e.errno == errno.ENODATA: response = None else: raise @@ -20,4 +19,4 @@ def getxattr(path: str, attr_name: str) -> bytes | None: def setxattr(path: str, attr_name: str, attr_value: bytes) -> None: _cached_attributes.pop((path, attr_name), None) - xattr.setxattr(path, attr_name, attr_value) + return os.setxattr(path, attr_name, attr_value) From 7c16e653470d099fd674682f8d49e50a8522492c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 7 May 2025 23:39:05 +0800 Subject: [PATCH 128/477] loggerd: remove redundant Params Construction (#35138) remove redundant Params Construction --- system/loggerd/loggerd.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/loggerd/loggerd.cc b/system/loggerd/loggerd.cc index 953ae1df32..2d2d4640ed 100644 --- a/system/loggerd/loggerd.cc +++ b/system/loggerd/loggerd.cc @@ -203,7 +203,7 @@ void handle_user_flag(LoggerdState *s) { // mark route for uploading Params params; - std::string routes = Params().get("AthenadRecentlyViewedRoutes"); + std::string routes = params.get("AthenadRecentlyViewedRoutes"); params.put("AthenadRecentlyViewedRoutes", routes + "," + s->logger.routeName()); prev_segment = s->logger.segment(); From 433e7268f5356b9cb08be0a95ccbf54bcafaec25 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 7 May 2025 18:12:01 +0200 Subject: [PATCH 129/477] Log register errors as errors (#35141) log register errors as errors --- selfdrive/pandad/pandad.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index db7dd387e0..fbf4e206ae 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -456,7 +456,12 @@ void pandad_run(std::vector &pandas) { for (auto *panda : pandas) { std::string log = panda->serial_read(); if (!log.empty()) { - LOGD("%s", log.c_str()); + if (log.find("Register 0x") != std::string::npos) { + // Log register divergent faults as errors + LOGE("%s", log.c_str()); + } else { + LOGD("%s", log.c_str()); + } } } From dcca094ad83664b7ff8e01f7d0d1ed40e315a8e3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 7 May 2025 15:29:41 -0700 Subject: [PATCH 130/477] Tesla: forward stock LKAS while disengaged (#35150) * bump * update docs * bump --- docs/CARS.md | 15 ++++++++++----- opendbc_repo | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 75917241d6..55871eba2f 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,13 +4,13 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 306 Supported Cars +# 311 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| |Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -33,17 +33,22 @@ A supported vehicle is one that just works when you install a comma device. All |Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Focus 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Focus Hybrid 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga Plug-in Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga Hybrid 2024|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/opendbc_repo b/opendbc_repo index 1754a76924..03b74b1f56 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 1754a769240f170b9033d802be549bd69e16b6c0 +Subproject commit 03b74b1f56830a18de91d5380d2508ce8c803eb8 From 2e0fa3f82720154a9078802653673124be9eb30a Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 7 May 2025 16:26:20 -0700 Subject: [PATCH 131/477] Tesla: allow enabling in tight curves (#35147) * bump * revert steer limit timer * alert for stock lkas * add enum * same as ldw * bump * draft * bump * bump * rm * why here?1 * bump to master --- opendbc_repo | 2 +- selfdrive/car/car_specific.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 03b74b1f56..c856a2c0bd 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 03b74b1f56830a18de91d5380d2508ce8c803eb8 +Subproject commit c856a2c0bd2b3c75f86a73b051c0c4cc7159559e diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index bbecc6e688..dc37519be1 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -191,7 +191,7 @@ class CarSpecificEvents: events.add(EventName.accFaulted) if CS.steeringPressed: events.add(EventName.steerOverride) - if CS.steeringDisengage: + if CS.steeringDisengage and not CS_prev.steeringDisengage: events.add(EventName.steerDisengage) if CS.brakePressed and CS.standstill: events.add(EventName.preEnableStandstill) From 36ff474bc812474b1fd15b1c5c58252563034fea Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 7 May 2025 16:44:20 -0700 Subject: [PATCH 132/477] remove `numpy.core` usage (#35152) fix --- system/hardware/tici/tests/test_power_draw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py index 669889a2ae..fd65ecd5cd 100644 --- a/system/hardware/tici/tests/test_power_draw.py +++ b/system/hardware/tici/tests/test_power_draw.py @@ -56,10 +56,10 @@ class TestPowerDraw: def valid_msg_count(self, proc, msg_counts): msgs_received = sum(msg_counts[msg] for msg in proc.msgs) msgs_expected = self.get_expected_messages(proc) - return np.core.numeric.isclose(msgs_expected, msgs_received, rtol=.02, atol=2) + return np.isclose(msgs_expected, msgs_received, rtol=.02, atol=2) def valid_power_draw(self, proc, used): - return np.core.numeric.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol) + return np.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol) def tabulate_msg_counts(self, msgs_and_power): msg_counts = defaultdict(int) From 0fb4aafa35896e831bd58a636a9a1bb4a1442743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Wed, 7 May 2025 18:06:41 -0700 Subject: [PATCH 133/477] Tomb Raider 7 (#35114) * Revert "Revert TR (#35110)" This reverts commit df4f2955dc16325472322522b12a8db5347f52fa. * eb5f884a-10ad-49fd-ae5c-e2818c26e568/400 * 1cc828ab-95e5-4620-aa07-b98918b4268d/400 * 5790a2c1-b487-4bef-a3c3-db1fcd5a756d/400 * raw plan --- selfdrive/controls/lib/longitudinal_planner.py | 15 ++++++++++++--- selfdrive/modeld/fill_model_msg.py | 10 +++++----- selfdrive/modeld/modeld.py | 14 +++++++++----- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- selfdrive/modeld/parse_model_outputs.py | 13 ++++++------- selfdrive/test/process_replay/ref_commit | 2 +- 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 9d71faeca2..4219e80f33 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -90,7 +90,7 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mpc.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' + self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: accel_coast = get_coast_accel(sm['carControl'].orientationNED[1]) @@ -113,7 +113,7 @@ class LongitudinalPlanner: # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - if self.mpc.mode == 'acc': + if self.mode == 'acc': accel_clip = [ACCEL_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP) @@ -160,8 +160,17 @@ class LongitudinalPlanner: self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 action_t = self.CP.longitudinalActuatorDelay + DT_MDL - output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, + output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, action_t=action_t, vEgoStopping=self.CP.vEgoStopping) + output_a_target_e2e = sm['modelV2'].action.desiredAcceleration + output_should_stop_e2e = sm['modelV2'].action.shouldStop + + if self.mode == 'acc': + output_a_target = output_a_target_mpc + self.output_should_stop = output_should_stop_mpc + else: + output_a_target = min(output_a_target_mpc, output_a_target_e2e) + self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc for idx in range(2): accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05) diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index a91c6395c7..36bd724d01 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -90,11 +90,11 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T) # temporal pose - temporal_pose = modelV2.temporalPose - temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() - temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() - temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() - temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() + #temporal_pose = modelV2.temporalPose + #temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() + #temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() + #temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() + #temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() # poly path fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 5e1acdea0e..84d7760dce 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -26,7 +26,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.selfdrive.modeld.constants import ModelConstants, Plan @@ -41,8 +41,8 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' -LAT_SMOOTH_SECONDS = 0.0 -LONG_SMOOTH_SECONDS = 0.0 +LAT_SMOOTH_SECONDS = 0.1 +LONG_SMOOTH_SECONDS = 0.3 MIN_LAT_CONTROL_SPEED = 0.3 @@ -55,7 +55,11 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log. action_t=long_action_t) desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS) - desired_curvature = model_output['desired_curvature'][0, 0] + desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2], + plan[:,Plan.ORIENTATION_RATE][:,2], + ModelConstants.T_IDXS, + v_ego, + lat_action_t) if v_ego > MIN_LAT_CONTROL_SPEED: desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) else: @@ -172,7 +176,7 @@ class ModelState: # TODO model only uses last value now self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] - self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs] + self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} if SEND_RAW_PRED: diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 3601bbb5da..5bd77bb958 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36 -size 15966721 +oid sha256:19e30484236efff72d519938c3e26461dbeb89c11d81fa7ecbff8e0263333c18 +size 15588463 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index aee6d8f1bf..0a7b4a803d 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624 -size 34882971 +oid sha256:dad289ae367cefcb862ef1d707fb4919d008f0eeaa1ebaf18df58d8de5a7d96e +size 46265585 diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 810c44ccb9..783572d436 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -88,6 +88,12 @@ class Parser: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) + for k in ['lead_prob', 'lane_lines_prob']: + self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) return outs @@ -95,17 +101,10 @@ class Parser: def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) - self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) if 'lat_planner_solution' in outs: self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) if 'desired_curvature' in outs: self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) - for k in ['lead_prob', 'lane_lines_prob']: - self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 1ae4c4a1a8..3dd3eab25f 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 +7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 \ No newline at end of file From 380f383e2e46cecfe491d559fe7656ad0b0bee9e Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 7 May 2025 18:13:53 -0700 Subject: [PATCH 134/477] ci: enable cache by default (#35121) * enable cache for Mac brew and scons * bump * save cache by default, explicitly opt-out * Delete bump-ci --------- Co-authored-by: Maxime Desroches --- .github/workflows/auto-cache/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-cache/action.yaml b/.github/workflows/auto-cache/action.yaml index fadd422b6e..377b1eedcd 100644 --- a/.github/workflows/auto-cache/action.yaml +++ b/.github/workflows/auto-cache/action.yaml @@ -12,7 +12,7 @@ inputs: required: true save: description: 'whether to save the cache' - default: 'false' + default: 'true' required: false outputs: cache-hit: From 2451d70408a8e289be69acfe4b1a155800a0cd4e Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 7 May 2025 18:21:53 -0700 Subject: [PATCH 135/477] AGNOS 12 (#35151) agnos 12 --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 24 ++++++------ system/hardware/tici/all-partitions.json | 50 ++++++++++++------------ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index aff451e541..9ce5013240 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="11.13" + export AGNOS_VERSION="12" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 1a3cd382a9..06f4d47be8 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -56,29 +56,29 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz", - "hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", - "hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", + "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", + "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf" + "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz", - "hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824", - "hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "size": 4404019200, + "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img.xz", + "hash": "92c2694de1c10aa7445f3b3a2f687f4a24f46c33202b1250e7909d1cb55c5c04", + "hash_raw": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", + "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533", + "ondevice_hash": "e88bece575f5515327b522f6fea8689842a62af8deace08bce8a640cc1ea8837", "alt": { - "hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img", - "size": 4404019200 + "hash": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", + "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img", + "size": 5368709120 } } ] \ No newline at end of file diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 4e24b229d4..cee5e7142f 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz", - "hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", - "hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892", + "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", + "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf" + "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz", - "hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824", - "hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "size": 4404019200, + "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img.xz", + "hash": "92c2694de1c10aa7445f3b3a2f687f4a24f46c33202b1250e7909d1cb55c5c04", + "hash_raw": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", + "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533", + "ondevice_hash": "e88bece575f5515327b522f6fea8689842a62af8deace08bce8a640cc1ea8837", "alt": { - "hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde", - "url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img", - "size": 4404019200 + "hash": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", + "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img", + "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108.img.xz", - "hash": "ff01a0ca5a2ea6661f836248043a211cd8d71c3269c139cb574b56855fabc3f4", - "hash_raw": "175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-8fc0fe410610acffb6b5cdeeb7af88950707bf0680b461e65a2ac5efc9ffecaa.img.xz", + "hash": "6520033c2b2529adf244a2da87e590250c532e6e1d5d7ff43df277a60c02ac10", + "hash_raw": "8fc0fe410610acffb6b5cdeeb7af88950707bf0680b461e65a2ac5efc9ffecaa", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "2f3d69e5015a45a18c3553f2edc5706aacd6d84a4b3d5010a3d76a1a3aa910b0" + "ondevice_hash": "82cdab812656e9cbb4a5016524c0227ab892bfcdc70a15a455678e524a7e723e" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b.img.xz", - "hash": "714970777e02bb53a71640735bdb84b3071ecbc0346b978ce12eb667d75634ec", - "hash_raw": "61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-681d3e40ac46c6331c004d22d3272ce2726a8dd51e12777c809d0c532d00cc6e.img.xz", + "hash": "3d73688e6f9f07d90b58a8357b9c94b952526bd2afa16a364b303096f57fa0ca", + "hash_raw": "681d3e40ac46c6331c004d22d3272ce2726a8dd51e12777c809d0c532d00cc6e", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "95e6889a808b8d266660990e67e917cf3b63179f23588565af7f2fa54f70ac76" + "ondevice_hash": "358079b4f340a7ecd74001dbbec06898b76df380ddb70b8ea0860efaf6786aa1" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997.img.xz", - "hash": "33e5ab398620f147b885a9627b2608591bd9e1c9aa481eb705dc86707d706ea2", - "hash_raw": "a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-fbcc9d296d63ac675f310b7785d94b44f5ef22c0304a3a0f7c2441f4bb910ca7.img.xz", + "hash": "44c3bab2ea45ac565bfd39cc64156a802c98b2dcb737bb2e7b0db2004b2872ca", + "hash_raw": "fbcc9d296d63ac675f310b7785d94b44f5ef22c0304a3a0f7c2441f4bb910ca7", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "cd6291dea40968123f7af0b831cbfbbd6e515b676f2e427ae47ff358f6ac148e" + "ondevice_hash": "fdfcf6bbca51faf601ec4da1dea1e4c21094d2db5d76945814fe5101716cbae1" } -] +] \ No newline at end of file From 47ed90c6cfe0147b35354dd9a1a07050060163a2 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 7 May 2025 19:11:37 -0700 Subject: [PATCH 136/477] Reapply "Mypy: Got passing on macos (#34591)" (#35126) (#35153) * Mypy: Got passing on macos (#34591) * Mypy: Got mypy passing on macos * common/realtime.py refactor * Mypy: mypy passing on darwin * Refactor: Removed else: pass statement * Refactor: Removed unnecessary check * added xattr to pyproject * loggerd: switched to xatter module * loggerd: removed unused module in xattr_cache.py * UV: update uv.lock * Update system/athena/athenad.py * athenad: fixed blank lines * loggerd: refactor of xattr_cache * cleanup --------- * fix getxattr no attribute on macOS * try fixing missing ENOATTR on Linux --------- Co-authored-by: Andrei Radulescu Co-authored-by: BrainLess <116778989+BrainLessPea@users.noreply.github.com> --- common/realtime.py | 5 +++-- system/athena/athenad.py | 7 +++++-- system/loggerd/xattr_cache.py | 11 ++++++----- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/common/realtime.py b/common/realtime.py index 82176a00a6..57926b4c4f 100644 --- a/common/realtime.py +++ b/common/realtime.py @@ -1,6 +1,7 @@ """Utilities for reading real time clocks and keeping soft real time constraints.""" import gc import os +import sys import time from setproctitle import getproctitle @@ -28,13 +29,13 @@ class Priority: def set_core_affinity(cores: list[int]) -> None: - if not PC: + if sys.platform == 'linux' and not PC: os.sched_setaffinity(0, cores) def config_realtime_process(cores: int | list[int], priority: int) -> None: gc.disable() - if not PC: + if sys.platform == 'linux' and not PC: os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority)) c = cores if isinstance(cores, list) else [cores, ] set_core_affinity(c) diff --git a/system/athena/athenad.py b/system/athena/athenad.py index 5813bc90a5..fb546fa40f 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -776,8 +776,11 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None: # While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s # offroad, we can expect to time out in 30 + (10 * 3) = 60s # FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) + if sys.platform == 'linux': + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30) + elif sys.platform == 'darwin': + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 7 if onroad else 30) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3) diff --git a/system/loggerd/xattr_cache.py b/system/loggerd/xattr_cache.py index d3220118ac..39bb172059 100644 --- a/system/loggerd/xattr_cache.py +++ b/system/loggerd/xattr_cache.py @@ -1,16 +1,17 @@ -import os import errno +import xattr + _cached_attributes: dict[tuple, bytes | None] = {} def getxattr(path: str, attr_name: str) -> bytes | None: key = (path, attr_name) if key not in _cached_attributes: try: - response = os.getxattr(path, attr_name) + response = xattr.getxattr(path, attr_name) except OSError as e: - # ENODATA means attribute hasn't been set - if e.errno == errno.ENODATA: + # ENODATA (Linux) or ENOATTR (macOS) means attribute hasn't been set + if e.errno == errno.ENODATA or (hasattr(errno, 'ENOATTR') and e.errno == errno.ENOATTR): response = None else: raise @@ -19,4 +20,4 @@ def getxattr(path: str, attr_name: str) -> bytes | None: def setxattr(path: str, attr_name: str, attr_value: bytes) -> None: _cached_attributes.pop((path, attr_name), None) - return os.setxattr(path, attr_name, attr_value) + xattr.setxattr(path, attr_name, attr_value) From 52669b6ad2ff025b73b4a10bc45f3066e0adefb8 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 7 May 2025 20:34:08 -0700 Subject: [PATCH 137/477] AGNOS 12.1 (#35154) 12.1 --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 12 ++++---- system/hardware/tici/all-partitions.json | 36 ++++++++++++------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 9ce5013240..73a7a89789 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12" + export AGNOS_VERSION="12.1" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 06f4d47be8..a415851a10 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,17 +67,17 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img.xz", - "hash": "92c2694de1c10aa7445f3b3a2f687f4a24f46c33202b1250e7909d1cb55c5c04", - "hash_raw": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", + "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz", + "hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939", + "hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "e88bece575f5515327b522f6fea8689842a62af8deace08bce8a640cc1ea8837", + "ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc", "alt": { - "hash": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", - "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img", + "hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", + "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index cee5e7142f..be303b7f90 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -350,51 +350,51 @@ }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img.xz", - "hash": "92c2694de1c10aa7445f3b3a2f687f4a24f46c33202b1250e7909d1cb55c5c04", - "hash_raw": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", + "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz", + "hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939", + "hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "e88bece575f5515327b522f6fea8689842a62af8deace08bce8a640cc1ea8837", + "ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc", "alt": { - "hash": "1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90", - "url": "https://commadist.azureedge.net/agnosupdate/system-1f9bab24bdc3a15dcf9feefa59b0e5e77c9a8db78950eb12f95eb2de56ebff90.img", + "hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", + "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-8fc0fe410610acffb6b5cdeeb7af88950707bf0680b461e65a2ac5efc9ffecaa.img.xz", - "hash": "6520033c2b2529adf244a2da87e590250c532e6e1d5d7ff43df277a60c02ac10", - "hash_raw": "8fc0fe410610acffb6b5cdeeb7af88950707bf0680b461e65a2ac5efc9ffecaa", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac.img.xz", + "hash": "b18001a2a87caa070fabf6321f8215ac353d6444564e3f86329b4dccc039ce54", + "hash_raw": "4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "82cdab812656e9cbb4a5016524c0227ab892bfcdc70a15a455678e524a7e723e" + "ondevice_hash": "15ce16f2349d5b4d5fec6ad1e36222b1ae744ed10b8930bc9af75bd244dccb3c" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-681d3e40ac46c6331c004d22d3272ce2726a8dd51e12777c809d0c532d00cc6e.img.xz", - "hash": "3d73688e6f9f07d90b58a8357b9c94b952526bd2afa16a364b303096f57fa0ca", - "hash_raw": "681d3e40ac46c6331c004d22d3272ce2726a8dd51e12777c809d0c532d00cc6e", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8.img.xz", + "hash": "12682cf54596ab1bd1c2464c4ca85888e4e06b47af5ff7d0432399e9907e2f64", + "hash_raw": "e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "358079b4f340a7ecd74001dbbec06898b76df380ddb70b8ea0860efaf6786aa1" + "ondevice_hash": "e4df9dea47ff04967d971263d50c17460ef240457e8d814e7c4f409f7493eb8a" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-fbcc9d296d63ac675f310b7785d94b44f5ef22c0304a3a0f7c2441f4bb910ca7.img.xz", - "hash": "44c3bab2ea45ac565bfd39cc64156a802c98b2dcb737bb2e7b0db2004b2872ca", - "hash_raw": "fbcc9d296d63ac675f310b7785d94b44f5ef22c0304a3a0f7c2441f4bb910ca7", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282.img.xz", + "hash": "fa471703be0f0647617d183312d5209d23407f1628e4ab0934e6ec54b1a6b263", + "hash_raw": "fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "fdfcf6bbca51faf601ec4da1dea1e4c21094d2db5d76945814fe5101716cbae1" + "ondevice_hash": "0b5b2402c9caa1ed7b832818e66580c974251e735bda91f2f226c41499d5616e" } ] \ No newline at end of file From f123e7ed7572355ebca588f5d506bd5ee52e9614 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 8 May 2025 22:19:28 +0800 Subject: [PATCH 138/477] CI: add system/ui to UI labeler (#35157) add system/ui to UI labeler --- .github/labeler.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yaml b/.github/labeler.yaml index db1f976da8..861c2efdbd 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -12,7 +12,7 @@ simulation: ui: - changed-files: - - any-glob-to-all-files: 'selfdrive/ui/**' + - any-glob-to-all-files: '{selfdrive/ui/**,system/ui/**}' tools: - changed-files: From e7f7675458baceeb20074f5ded16fc94d473cbf8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 8 May 2025 23:09:44 +0800 Subject: [PATCH 139/477] micd: fix thread safety by adding locking for shared state (#35148) * fix thread safety by adding locking for shared state * Update system/micd.py --------- Co-authored-by: Cameron Clough --- system/micd.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/system/micd.py b/system/micd.py index af1aa31360..38f3225f55 100755 --- a/system/micd.py +++ b/system/micd.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import numpy as np from functools import cache +import threading from cereal import messaging from openpilot.common.realtime import Ratekeeper @@ -52,12 +53,18 @@ class Mic: self.sound_pressure_weighted = 0 self.sound_pressure_level_weighted = 0 - def update(self): - msg = messaging.new_message('microphone', valid=True) - msg.microphone.soundPressure = float(self.sound_pressure) - msg.microphone.soundPressureWeighted = float(self.sound_pressure_weighted) + self.lock = threading.Lock() - msg.microphone.soundPressureWeightedDb = float(self.sound_pressure_level_weighted) + def update(self): + with self.lock: + sound_pressure = self.sound_pressure + sound_pressure_weighted = self.sound_pressure_weighted + sound_pressure_level_weighted = self.sound_pressure_level_weighted + + msg = messaging.new_message('microphone', valid=True) + msg.microphone.soundPressure = float(sound_pressure) + msg.microphone.soundPressureWeighted = float(sound_pressure_weighted) + msg.microphone.soundPressureWeightedDb = float(sound_pressure_level_weighted) self.pm.send('microphone', msg) self.rk.keep_time() @@ -69,17 +76,17 @@ class Mic: Logged A-weighted equivalents are rough approximations of the human-perceived loudness. """ + with self.lock: + self.measurements = np.concatenate((self.measurements, indata[:, 0])) - self.measurements = np.concatenate((self.measurements, indata[:, 0])) + while self.measurements.size >= FFT_SAMPLES: + measurements = self.measurements[:FFT_SAMPLES] - while self.measurements.size >= FFT_SAMPLES: - measurements = self.measurements[:FFT_SAMPLES] + self.sound_pressure, _ = calculate_spl(measurements) + measurements_weighted = apply_a_weighting(measurements) + self.sound_pressure_weighted, self.sound_pressure_level_weighted = calculate_spl(measurements_weighted) - self.sound_pressure, _ = calculate_spl(measurements) - measurements_weighted = apply_a_weighting(measurements) - self.sound_pressure_weighted, self.sound_pressure_level_weighted = calculate_spl(measurements_weighted) - - self.measurements = self.measurements[FFT_SAMPLES:] + self.measurements = self.measurements[FFT_SAMPLES:] @retry(attempts=7, delay=3) def get_stream(self, sd): From 73ee0c022f240eb30f28578620a09459c0ae3afc Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 8 May 2025 16:18:01 +0100 Subject: [PATCH 140/477] Reapply "ui(raylib): create BaseWindow (#35074)" (#35077) * Reapply "ui(raylib): create BaseWindow (#35074)" This reverts commit 83b84a5bec719c3e8cc1501d56344d95d4a9c4f4. * correct title * error msg * cloudlog --- system/ui/lib/window.py | 58 +++++++++++++++++++++++++++++++++++++++++ system/ui/spinner.py | 42 ++++------------------------- system/ui/text.py | 45 ++++---------------------------- 3 files changed, 68 insertions(+), 77 deletions(-) create mode 100644 system/ui/lib/window.py diff --git a/system/ui/lib/window.py b/system/ui/lib/window.py new file mode 100644 index 0000000000..989a3b0284 --- /dev/null +++ b/system/ui/lib/window.py @@ -0,0 +1,58 @@ +import threading +import time +import os +from typing import Generic, Protocol, TypeVar +from openpilot.common.swaglog import cloudlog +from openpilot.system.ui.lib.application import gui_app + + +class RendererProtocol(Protocol): + def render(self): ... + + +R = TypeVar("R", bound=RendererProtocol) + + +class BaseWindow(Generic[R]): + def __init__(self, title: str): + self._title = title + self._renderer: R | None = None + self._stop_event = threading.Event() + self._thread = threading.Thread(target=self._run) + self._thread.start() + + # wait for the renderer to be initialized + while self._renderer is None and self._thread.is_alive(): + time.sleep(0.01) + + def _create_renderer(self) -> R: + raise NotImplementedError() + + def _run(self): + if os.getenv("CI") is not None: + return + gui_app.init_window(self._title) + self._renderer = self._create_renderer() + try: + for _ in gui_app.render(): + if self._stop_event.is_set(): + break + self._renderer.render() + finally: + gui_app.close() + + def __enter__(self): + return self + + def close(self): + if self._thread.is_alive(): + self._stop_event.set() + self._thread.join(timeout=2.0) + if self._thread.is_alive(): + cloudlog.warning(f"Failed to join {self._title} thread") + + def __del__(self): + self.close() + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 5da96c73f9..119bdba3e7 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -6,6 +6,7 @@ import time from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.window import BaseWindow from openpilot.system.ui.text import wrap_text # Constants @@ -85,16 +86,12 @@ class SpinnerRenderer: FONT_SIZE, 0.0, rl.WHITE) -class Spinner: +class Spinner(BaseWindow[SpinnerRenderer]): def __init__(self): - self._renderer: SpinnerRenderer | None = None - self._stop_event = threading.Event() - self._thread = threading.Thread(target=self._run) - self._thread.start() + super().__init__("Spinner") - # wait for the renderer to be initialized - while self._renderer is None and self._thread.is_alive(): - time.sleep(0.01) + def _create_renderer(self): + return SpinnerRenderer() def update(self, spinner_text: str): if self._renderer is not None: @@ -103,35 +100,6 @@ class Spinner: def update_progress(self, cur: float, total: float): self.update(str(round(100 * cur / total))) - def _run(self): - if os.getenv("CI") is not None: - return - gui_app.init_window("Spinner") - self._renderer = renderer = SpinnerRenderer() - try: - for _ in gui_app.render(): - if self._stop_event.is_set(): - break - renderer.render() - finally: - gui_app.close() - - def __enter__(self): - return self - - def close(self): - if self._thread.is_alive(): - self._stop_event.set() - self._thread.join(timeout=2.0) - if self._thread.is_alive(): - print("WARNING: failed to join spinner thread") - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - if __name__ == "__main__": with Spinner() as s: diff --git a/system/ui/text.py b/system/ui/text.py index e57ae45d9a..33e8167c64 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -1,13 +1,12 @@ #!/usr/bin/env python3 -import os import re -import threading import time import pyray as rl from openpilot.system.hardware import HARDWARE, PC from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.window import BaseWindow MARGIN = 50 SPACING = 40 @@ -74,52 +73,18 @@ class TextWindowRenderer: return ret -class TextWindow: +class TextWindow(BaseWindow[TextWindowRenderer]): def __init__(self, text: str): self._text = text + super().__init__("Text") - self._renderer: TextWindowRenderer | None = None - self._stop_event = threading.Event() - self._thread = threading.Thread(target=self._run) - self._thread.start() - - # wait for the renderer to be initialized - while self._renderer is None and self._thread.is_alive(): - time.sleep(0.01) + def _create_renderer(self): + return TextWindowRenderer(self._text) def wait_for_exit(self): while self._thread.is_alive(): time.sleep(0.01) - def _run(self): - if os.getenv("CI") is not None: - return - gui_app.init_window("Text") - self._renderer = renderer = TextWindowRenderer(self._text) - try: - for _ in gui_app.render(): - if self._stop_event.is_set(): - break - renderer.render() - finally: - gui_app.close() - - def __enter__(self): - return self - - def close(self): - if self._thread.is_alive(): - self._stop_event.set() - self._thread.join(timeout=2.0) - if self._thread.is_alive(): - print("WARNING: failed to join text window thread") - - def __del__(self): - self.close() - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - if __name__ == "__main__": with TextWindow(DEMO_TEXT): From 33849245d8025786c97881a8c132989023bb62b7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 8 May 2025 23:31:06 +0800 Subject: [PATCH 141/477] uploader.py: fix empty string handing in AthenadRecentlyViewedRoutes parameter (#35139) Fix empty strings in AthenadRecentlyViewedRoutes parameter --- system/loggerd/uploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index c1bf804880..6e6df6114e 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -89,7 +89,7 @@ class Uploader: def list_upload_files(self, metered: bool) -> Iterator[tuple[str, str, str]]: r = self.params.get("AthenadRecentlyViewedRoutes", encoding="utf8") - requested_routes = [] if r is None else r.split(",") + requested_routes = [] if r is None else [route for route in r.split(",") if route] for logdir in listdir_by_creation(self.root): path = os.path.join(self.root, logdir) From 037695af4a1a17d37a22595ad139781616857209 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 8 May 2025 16:04:59 -0400 Subject: [PATCH 142/477] Car interface: pass into setup interfaces on init (#887) * Car interface: pass into setup interfaces on init * more * should be after --- selfdrive/car/card.py | 2 +- selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/controls/lib/tests/test_latcontrol.py | 2 +- sunnypilot/selfdrive/car/interfaces.py | 13 ++++++++----- .../controls/lib/nnlc/tests/test_fingerprint.py | 2 +- .../controls/lib/nnlc/tests/test_load_model.py | 2 +- .../selfdrive/controls/lib/nnlc/tests/test_nnlc.py | 2 +- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 2326f6fbc7..fa4b10e0d1 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -108,7 +108,7 @@ class Car: fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle", encoding='utf-8') or "{}").get("platform", None) self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, num_pandas, cached_params, fixed_fingerprint) - sunnypilot_interfaces.setup_interfaces(self.CI.CP, self.CI.CP_SP, self.params) + sunnypilot_interfaces.setup_interfaces(self.CI, self.params) self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP) self.CP = self.CI.CP self.CP_SP = self.CI.CP_SP diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index fdb823f5f8..75e5467b7e 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -45,9 +45,9 @@ class TestCarInterfaces: alpha_long=args['alpha_long'], docs=False) car_params_sp = CarInterface.get_params_sp(car_params, car_name, args['fingerprints'], args['car_fw'], alpha_long=args['alpha_long'], docs=False) - sunnypilot_interfaces.setup_interfaces(car_params, car_params_sp) car_params = car_params.as_reader() car_interface = CarInterface(car_params, car_params_sp) + sunnypilot_interfaces.setup_interfaces(car_interface) assert car_params assert car_params_sp assert car_interface diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py index 3bf9be9d92..727f68195f 100644 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/lib/tests/test_latcontrol.py @@ -23,7 +23,7 @@ class TestLatControl: CP = CarInterface.get_non_essential_params(car_name) CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name) CI = CarInterface(CP, CP_SP) - sunnypilot_interfaces.setup_interfaces(CP, CP_SP) + sunnypilot_interfaces.setup_interfaces(CI) CP_SP = convert_to_capnp(CP_SP) VM = VehicleModel(CP) diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index 5fac76c283..2c27e960e9 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -26,8 +26,8 @@ def log_fingerprint(CP: structs.CarParams) -> None: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) -def _initialize_neural_network_lateral_control(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None, - enabled: bool = False) -> None: +def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, + params: Params = None, enabled: bool = False) -> None: if params is None: params = Params() @@ -40,7 +40,7 @@ def _initialize_neural_network_lateral_control(CP: structs.CarParams, CP_SP: str enabled = params.get_bool("NeuralNetworkLateralControl") if enabled: - CarInterfaceBase.configure_torque_tune(CP.carFingerprint, CP.lateralTuning) + CI.configure_torque_tune(CP.carFingerprint, CP.lateralTuning) CP_SP.neuralNetworkLateralControl.model.path = nnlc_model_path CP_SP.neuralNetworkLateralControl.model.name = nnlc_model_name @@ -61,8 +61,11 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, CP.radarUnavailable = False -def setup_interfaces(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None): - _initialize_neural_network_lateral_control(CP, CP_SP, params) +def setup_interfaces(CI: CarInterfaceBase, params: Params = None): + CP = CI.CP + CP_SP = CI.CP_SP + + _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) _initialize_radar_tracks(CP, CP_SP, params) diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_fingerprint.py b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_fingerprint.py index 97f927502a..0f016ccaf0 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_fingerprint.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_fingerprint.py @@ -24,7 +24,7 @@ class TestNNLCFingerprintBase: CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name) CI = CarInterface(CP, CP_SP) - sunnypilot_interfaces.setup_interfaces(CP, CP_SP, Params()) + sunnypilot_interfaces.setup_interfaces(CI, Params()) return CI diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_load_model.py b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_load_model.py index 38fd994831..af7077d1c6 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_load_model.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_load_model.py @@ -22,7 +22,7 @@ class TestNNTorqueModel: CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name) CI = CarInterface(CP, CP_SP) - sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params) + sunnypilot_interfaces.setup_interfaces(CI, params) CP_SP = convert_to_capnp(CP_SP) diff --git a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py index 4e2649f2ff..699327cd81 100644 --- a/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py +++ b/sunnypilot/selfdrive/controls/lib/nnlc/tests/test_nnlc.py @@ -51,7 +51,7 @@ class TestNeuralNetworkLateralControl: CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name) CI = CarInterface(CP, CP_SP) - sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params) + sunnypilot_interfaces.setup_interfaces(CI, params) CP_SP = convert_to_capnp(CP_SP) VM = VehicleModel(CP) From 4bbbe3d2d1ff67eb6f16adb15115c554389d8f12 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 8 May 2025 22:22:53 +0100 Subject: [PATCH 143/477] ui(raylib): revert fps to 60 (#35163) bump default fps --- system/ui/lib/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a3e209c394..eae5962277 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -7,7 +7,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE -DEFAULT_FPS = 30 +DEFAULT_FPS = 60 FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions From dde9c703f35509e6ebd46cee92fc75b955b1f252 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 9 May 2025 05:31:00 +0800 Subject: [PATCH 144/477] cabana: optimize get_raw_value() function for CAN signal extraction (#35137) optimize get_raw_value() for CAN signal extraction --- tools/cabana/dbc/dbc.cc | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/tools/cabana/dbc/dbc.cc b/tools/cabana/dbc/dbc.cc index ba979f258c..e9c869fce7 100644 --- a/tools/cabana/dbc/dbc.cc +++ b/tools/cabana/dbc/dbc.cc @@ -180,25 +180,36 @@ bool cabana::Signal::operator==(const cabana::Signal &other) const { // helper functions double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal &sig) { - int64_t val = 0; + const int msb_byte = sig.msb / 8; + if (msb_byte >= (int)data_size) return 0; - int i = sig.msb / 8; - int bits = sig.size; - while (i >= 0 && i < data_size && bits > 0) { - int lsb = (int)(sig.lsb / 8) == i ? sig.lsb : i * 8; - int msb = (int)(sig.msb / 8) == i ? sig.msb : (i + 1) * 8 - 1; - int size = msb - lsb + 1; + const int lsb_byte = sig.lsb / 8; + uint64_t val = 0; - uint64_t d = (data[i] >> (lsb - (i * 8))) & ((1ULL << size) - 1); - val |= d << (bits - size); - - bits -= size; - i = sig.is_little_endian ? i - 1 : i + 1; + // Fast path: signal fits in a single byte + if (msb_byte == lsb_byte) { + val = (data[msb_byte] >> (sig.lsb & 7)) & ((1ULL << sig.size) - 1); + } else { + // Multi-byte case: signal spans across multiple bytes + int bits = sig.size; + int i = msb_byte; + const int step = sig.is_little_endian ? -1 : 1; + while (i >= 0 && i < (int)data_size && bits > 0) { + const int msb = (i == msb_byte) ? sig.msb & 7 : 7; + const int lsb = (i == lsb_byte) ? sig.lsb & 7 : 0; + const int nbits = msb - lsb + 1; + val = (val << nbits) | ((data[i] >> lsb) & ((1ULL << nbits) - 1)); + bits -= nbits; + i += step; + } } - if (sig.is_signed) { - val -= ((val >> (sig.size - 1)) & 0x1) ? (1ULL << sig.size) : 0; + + // Sign extension (if needed) + if (sig.is_signed && (val & (1ULL << (sig.size - 1)))) { + val |= ~((1ULL << sig.size) - 1); } - return val * sig.factor + sig.offset; + + return static_cast(val) * sig.factor + sig.offset; } void updateMsbLsb(cabana::Signal &s) { From ff63d177238705a4b81cc6fc98c0e8a023c1d45a Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 00:46:18 -0400 Subject: [PATCH 145/477] ui: emit panel refresh signal from Platform Selector (#891) --- .../qt/offroad/settings/vehicle/platform_selector.cc | 1 + .../sunnypilot/qt/offroad/settings/vehicle/platform_selector.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc index d4950bf2a8..83fa0b1698 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc @@ -62,6 +62,7 @@ void PlatformSelector::refresh(bool _offroad) { } } setEnabled(true); + emit refreshPanel(); offroad = _offroad; } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h index 8a61832009..3d912f822c 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h @@ -19,6 +19,9 @@ public: public slots: void refresh(bool _offroad); +signals: + void refreshPanel(); + private: void searchPlatforms(const QString &query); void setPlatform(const QString &platform = ""); From 72f09ec9f5a8bd8695c9dc50bd5fe9bfccc3d9b1 Mon Sep 17 00:00:00 2001 From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Date: Thu, 8 May 2025 22:06:45 -0700 Subject: [PATCH 146/477] Hyundai: custom longitudinal tuning (#658) * Maybe I was asleep but somehow it worked. * Maybe I was asleep but somehow it worked. * 70/30 split * 60/35 split * 67:33 split * use Sqrt for tiny values to increase factor. Oh, also add a test file. * use Sqrt for tiny values to increase factor. Oh, also add a test file. * use Sqrt for tiny values to increase factor. Oh, also add a test file. * Raise these to restart tests * Raise these to restart tests * Update jerk calculation mimicking j_ego math. * README.md * Update timestep * Fix test * Readability * Lower time * Add higher limits for CANFD * Send these as a floating object. update comments to add TODO msg. * Conditional flags * yes its a little weird but heres why: ` "aReqValue": long_state.accel_value if enabled else a_val` its only to not have merge conflicts with syncs from this file, because if not enabled a_val is already at 0 anyways. * Make it easier to read * Chronological order * Trigger rebuild * This is an internal test on my end. Disregard, for now * fw version for my car... * remove fw version as that was a test * bump submodules * refactor that while passing internal tests * bump safety mutation test * all should match 50 Hz * bump safety mutation test * match stock behavior for accel * do our own clipping * apply upper/lower dynamically * bump test * bump test * Higher lower limit * move desired accel force zero in tuning controller * bump * bump * abs it here * fix logic * make sure it resets to 0, enforced in safety * Test a few things * Higher jerk for VOACC * Elantra specific * raise `off` to stock * bump opendbc test * allow jerk calculations at all times * bump * bump * match aReqVale * we dont need such high accel jerk when above 20m/s. This is cruise control, not a racecar. * 0.45 default long actuator delay * align type hint * add lower jerk multiplier for HYUNDAI_IONIQ * Change min jerk * should be float * Change min jerk * Change min jerk upper * Change max jerk upper * Change max jerk upper * Change max jerk upper * step + threshold + first order filter to smooth * constants * nah * multiplier to 1.5 * step updates for parabolic accel/braking * step updates for parabolic accel/braking * even less * test multiplier * Merge remote-tracking branch 'sunnypilot/opendbc/master-new' into HKG-long-tune * back to default delay for now * update test * update config * update test * update config.py * update test * Remove stoppingDecelRate **No need for this anymore** * multiplier to 1.0 * Variable lower dependency * Variable lower dependency * Variable lower jerk minimum * Variable lower jerk minimum planned_accel -> desired_acel * Revert some scaling * .01 here * .01 here * try even smoother stopping * missed a `tuning` here that instead calls itself to inf. * what is math * Red Diff * Red Diff * bring some back * bring some back * stronger when needed * something simple * simplify more * simplify more * dont use future * whoops * why * reset * Refactor longitudinal controller for better jerk handling Revised `calculate_a_value` and jerk processing logic to improve acceleration and jerk handling, particularly under dynamic conditions. Adjusted test cases to cover the new logic. * Refactor longitudinal controller for better jerk handling Revised `calculate_a_value` and jerk processing logic to improve acceleration and jerk handling, particularly under dynamic conditions. Adjusted test cases to cover the new logic. * Refactor longitudinal controller for better jerk handling Revised `calculate_a_value` and jerk processing logic to improve acceleration and jerk handling, particularly under dynamic conditions. Adjusted test cases to cover the new logic. * Adjust lower_jerk interpolation for improved accuracy * Lowered the acceleration error threshold from -0.01 to -0.001 * Adjust jerk interpolation thresholds for Hyundai tuning. Updated the interpolation range for smaller acceleration errors. * Move it up * -.03 not -.05 * radarUnavailable = higher limit patch * Commit the real change * Updated the interpolation values and logic for lower_jerk to refine acceleration behavior. * Updated the interpolation values and logic for lower_jerk to refine acceleration behavior. * Updated the interpolation values and logic for lower_jerk to refine acceleration behavior. * Updated the interpolation values and logic for lower_jerk to refine acceleration behavior. * THIS. THIS WILL STOP FOR LEADS * Higher for VOACC * Higher for VOACC * Rename `make_jerk` to `calculate_jerk` in test cases. * Adjust jerk tuning for Hyundai longitudinal control Updated the interpolation table for lower jerk values to improve deceleration handling. This adjustment refines control behavior during higher deceleration requests. * Updated the interpolation breakpoint from -0.03 to -0.025 * Adjust longitudinal control jerk tuning for smoother response Updated interpolation values in the jerk tuning logic to improve acceleration smoothing in Hyundai longitudinal control. These changes aim to provide a more consistent and predictable driving experience, particularly during deceleration scenarios. * Handle radar unavailability in longitudinal tuning check * Introduce `toggleDisableMsg` to streamline determining toggle states and descriptions. * `sunnypilot` * `Refactor longitudinal control acceleration and jerk handling` Removed unused `FirstOrderFilter` and simplified acceleration and jerk calculations for improved clarity and maintainability. Adjusted logic to replace redundant return statements, streamline jerk computation, and reduce unnecessary dependencies. * `Refactor longitudinal control acceleration and jerk handling` Removed unused `FirstOrderFilter` and simplified acceleration and jerk calculations for improved clarity and maintainability. Adjusted logic to replace redundant return statements, streamline jerk computation, and reduce unnecessary dependencies. * Simplify test to be straight to the point. * Adjust upper_speed_factor and integrate ramp_update logic * Adjust upper_speed_factor and integrate ramp_update logic * Adjust upper_speed_factor and integrate ramp_update logic * Refine acceleration and jerk tuning logic. * greater than 0 * bump * bring back ramp update for jerk upper * Update opendbc_repo * Revert "Update opendbc_repo" This reverts commit b790387c9080b3a553694fbae850e9aa8bb6e125. * bump * bump * bump * using count seems to work better * Refine jerk tuning logic and expand test coverage * bump vals * fix logic * Adjust jerk tuning parameters for Hyundai longitudinal control * retry lfs check * bump submodule * Fix acceleration blending and enhance test coverage * Update lower_jerk breakpoints * bump * Adjust lower jerk parameters and include accel_cmd condition * Adjust vars * "Adjust Hyundai longitudinal jerk limits and tuning logic" * Adjust jerk limits and tuning logic for better control * Update Hyundai longitudinal tuning and add dynamic jerk logic Refactored longitudinal configuration to include "lookahead" parameters and revised "jerk_limits" for improved tuning flexibility. Implemented dynamic jerk adaptation logic (Gen1) for smoother braking and acceleration transitions. * Adjust Hyundai longitudinal control parameters for tuning * bump to original branch * QSize Policy to stretch toggle to screen size * bump * completely blocked xD * Sunnypilot -> openpilot * Whitespace * Clean up toggle name * Still allow ramp while in standstill for EV * Adjust Kia Niro EV tuning parameters for smoother control * self.cp.flags * bump submodule * bump * Remove ramp for lower desired jerk * update README.md * update README.md * Adjust lookahead_jerk_lower_v values in Hyundai longitudinal config. * update tuning * Update tune one last time to vals from device which are much smoother. * Accel error * bump * fix * rename flag and slight cleanup * start ui stuff * long_state -> tuning * Prep for clean up * small changes * less in main * no longer * format * bump * init panel * expose signal for other panels to interact with * split into its own * unused * move around * actually modify CP_SP * fix offroad transition * rename * back * don't update unless we're looking at it * move around * use min length * do this * whoops * move * bump * bump * this is fine * bump --------- Co-authored-by: Jason Wen Co-authored-by: DevTekVE Co-authored-by: royjr --- common/params_keys.h | 1 + opendbc_repo | 2 +- selfdrive/ui/sunnypilot/SConscript | 1 + .../settings/vehicle/hyundai_settings.cc | 84 +++++++++++++++++++ .../settings/vehicle/hyundai_settings.h | 32 +++++++ .../qt/offroad/settings/vehicle_panel.cc | 25 ++++++ .../qt/offroad/settings/vehicle_panel.h | 14 +++- sunnypilot/selfdrive/car/interfaces.py | 19 ++++- system/manager/manager.py | 1 + 9 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h diff --git a/common/params_keys.h b/common/params_keys.h index 575dcc2adc..97f23d5108 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -164,6 +164,7 @@ inline static std::unordered_map keys = { {"BackupManager_RestoreVersion", PERSISTENT}, // sunnypilot car specific params + {"HyundaiLongitudinalTuning", PERSISTENT}, {"HyundaiRadarTracks", PERSISTENT}, {"HyundaiRadarTracksConfirmed", PERSISTENT}, {"HyundaiRadarTracksPersistent", PERSISTENT}, diff --git a/opendbc_repo b/opendbc_repo index 029bc6abc4..95fc304727 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 029bc6abc43e497fd361bbcbb9efb92e43353c5b +Subproject commit 95fc304727483d0d7f18f7df348d941046e046f2 diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 4ff3f70477..82a257523e 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -50,6 +50,7 @@ network_src = [ ] vehicle_panel_qt_src = [ + "sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc", "sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc", ] diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc new file mode 100644 index 0000000000..08d76f5927 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +HyundaiSettings::HyundaiSettings(QWidget *parent) : QWidget(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + std::vector tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") }; + longitudinalTuningToggle = new ButtonParamControlSP( + "HyundaiLongitudinalTuning", + tr("Custom Longitudinal Tuning"), + tr("Select a tuning mode.\n" + "Off: no custom tuning applied.\n" + "Dynamic: on-the-spot adjustments using dynamic calculations.\n" + "Predictive: adjusts based on anticipated ACC variation."), + "", + tuning_texts, + 500 + ); + longitudinalTuningToggle->showDescription(); + longitudinalTuningToggle->setProperty("originalDesc", longitudinalTuningToggle->getDescription()); + list->addItem(longitudinalTuningToggle); + + QObject::connect(uiState(), &UIState::offroadTransition, this, &HyundaiSettings::updateSettings); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +QString HyundaiSettings::toggleDisableMsg() const { + if (!has_longitudinal_control) { + return tr("This feature can only be used with openpilot longitudinal control enabled."); + } + + if (!offroad) { + return tr("Enable \"Always Offroad\" in Device panel, or turn vehicle off to select an option."); + } + + return QString(); +} + +void HyundaiSettings::showEvent(QShowEvent *event) { + updateSettings(offroad); +} + +void HyundaiSettings::updateSettings(bool _offroad) { + if (!isVisible()) { + return; + } + + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + has_longitudinal_control = hasLongitudinalControl(CP); + + QString longitudinal_tuning_disabled_msg = toggleDisableMsg(); + if (!longitudinal_tuning_disabled_msg.isEmpty()) { + longitudinalTuningToggle->setEnabled(false); + longitudinalTuningToggle->setDescription(longitudinal_tuning_disabled_msg); + } else { + longitudinalTuningToggle->setEnabled(true); + longitudinalTuningToggle->setDescription(longitudinalTuningToggle->property("originalDesc").toString()); + } + + longitudinalTuningToggle->showDescription(); + } else { + has_longitudinal_control = false; + longitudinalTuningToggle->setEnabled(false); + } + + offroad = _offroad; +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h new file mode 100644 index 0000000000..3137a0d1b9 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class HyundaiSettings : public QWidget { + Q_OBJECT + +public: + explicit HyundaiSettings(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + +public slots: + void updateSettings(bool _offroad); + +private: + Params params; + bool offroad = false; + bool has_longitudinal_control = false; + + ButtonParamControlSP *longitudinalTuningToggle = nullptr; + QString toggleDisableMsg() const; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc index cc3e6df183..bc9f645f3b 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc @@ -7,6 +7,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { @@ -18,11 +19,16 @@ VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { vlayout->setContentsMargins(50, 20, 50, 20); platformSelector = new PlatformSelector(); + QObject::connect(platformSelector, &PlatformSelector::refreshPanel, this, &VehiclePanel::updateBrandSettings); list->addItem(platformSelector); ScrollViewSP *scroller = new ScrollViewSP(list, this); vlayout->addWidget(scroller); + hyundaiSettings = new HyundaiSettings(this); + vlayout->addWidget(hyundaiSettings); + hyundaiSettings->setVisible(false); + QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel); main_layout->addWidget(vehicleScreen); @@ -36,5 +42,24 @@ void VehiclePanel::showEvent(QShowEvent *event) { void VehiclePanel::updatePanel(bool _offroad) { platformSelector->refresh(_offroad); + updateBrandSettings(); + offroad = _offroad; } + +void VehiclePanel::updateBrandSettings() { + if (!isVisible()) { + return; + } + + resetBrandSettings(); + + QString brand = platformSelector->getPlatformBundle("brand").toString(); + if (brand == "hyundai") { + hyundaiSettings->setVisible(true); + } +} + +void VehiclePanel::resetBrandSettings() { + hyundaiSettings->setVisible(false); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h index f6265da7a6..5548ff7181 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h @@ -9,6 +9,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h" class VehiclePanel : public QFrame { @@ -22,8 +23,17 @@ public slots: void updatePanel(bool _offroad); private: + void resetBrandSettings(); + QStackedLayout* main_layout = nullptr; QWidget* vehicleScreen = nullptr; - PlatformSelector *platformSelector = nullptr; - bool offroad; + PlatformSelector* platformSelector = nullptr; + + // brand panels + HyundaiSettings* hyundaiSettings = nullptr; + + bool offroad = false; + +private slots: + void updateBrandSettings(); }; diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index 2c27e960e9..f2019f5ed7 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -11,6 +11,7 @@ from opendbc.car.car_helpers import can_fingerprint from opendbc.car.interfaces import CarInterfaceBase from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC +from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog @@ -25,6 +26,21 @@ def log_fingerprint(CP: structs.CarParams) -> None: else: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) +def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, + params: Params = None) -> None: + if params is None: + params = Params() + + # Hyundai Custom Longitudinal Tuning + if CP.brand == 'hyundai': + hyundai_longitudinal_tuning = int(params.get("HyundaiLongitudinalTuning", encoding="utf8") or 0) + if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC: + CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value + if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE: + CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value + + CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP) + def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None, enabled: bool = False) -> None: @@ -61,10 +77,11 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, CP.radarUnavailable = False -def setup_interfaces(CI: CarInterfaceBase, params: Params = None): +def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None: CP = CI.CP CP_SP = CI.CP_SP + _initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params) _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) _initialize_radar_tracks(CP, CP_SP, params) diff --git a/system/manager/manager.py b/system/manager/manager.py index fde85f0f7c..90df415a8c 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -45,6 +45,7 @@ def manager_init() -> None: ("AutoLaneChangeTimer", "0"), ("AutoLaneChangeBsmDelay", "0"), ("DynamicExperimentalControl", "0"), + ("HyundaiLongitudinalTuning", "0"), ("Mads", "1"), ("MadsMainCruiseAllowed", "1"), ("MadsPauseLateralOnBrake", "0"), From 4a094ef56f07fb1226b13636bffa17614131e9ed Mon Sep 17 00:00:00 2001 From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Date: Thu, 8 May 2025 22:19:35 -0700 Subject: [PATCH 147/477] Models: Enable support for liveDelay in modeld_v2 (#879) Enable support for liveDelay in modeld_v2 --- sunnypilot/modeld_v2/modeld.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index 9a90e21cb5..e0f38c59ac 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -26,6 +26,7 @@ from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants from openpilot.sunnypilot.modeld_v2.model_runner import ONNXRunner, TinygradRunner PROCESS_NAME = "selfdrive.modeld.modeld" +LAT_SMOOTH_SECONDS = 0.0 class FrameMeta: @@ -163,7 +164,7 @@ def main(demo=False): # messaging pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) - sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"]) + sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) publish_state = PublishState() params = Params() @@ -188,8 +189,8 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) - # TODO this needs more thought, use .2s extra for now to estimate other delays - steer_delay = CP.steerActuatorDelay + .2 + # Enable lagd support for modeld_v2 + steer_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS DH = DesireHelper() From f09a3b32d1b0a7d997b5ac76df048bce20ed8499 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 9 May 2025 01:35:10 -0400 Subject: [PATCH 148/477] UI: Fix Padding on `OptionControlSP` (#886) fix padding Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/qt/widgets/controls.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc index 09ab5417ed..551907c612 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc @@ -118,7 +118,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con if (!title.isEmpty()) { title_label = new QPushButton(title); title_label->setFixedHeight(120); - title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 20 0 0 0"); + title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 0 0 0 0"); main_layout->addWidget(title_label, 1); connect(title_label, &QPushButton::clicked, [=]() { From 6de9526d4d316b1269613ee95b9d62a5cbfff043 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 9 May 2025 05:37:59 -0400 Subject: [PATCH 149/477] bug: fix braking issue while experimental and using old model (#881) * Fixes an issue on the long planner since Tomb Raider models, where the models are now meant to output the acceleration target and the "should stop" instead of it being calculated. However, older models (particularly those running on modeld_v2 from SP) do not output this. Leading to a "coasting" situation instead of braking when only e2e is used which is totally wrong. --- selfdrive/controls/lib/longitudinal_planner.py | 4 ++++ sunnypilot/models/helpers.py | 2 +- sunnypilot/selfdrive/controls/lib/longitudinal_planner.py | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 4e842f8d1b..377ce7c2cb 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -178,6 +178,10 @@ class LongitudinalPlanner(LongitudinalPlannerSP): output_a_target = min(output_a_target_mpc, output_a_target_e2e) self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc + if not self.is_stock: + # To support non Tomb Raider models + output_a_target, self.output_should_stop = output_a_target_mpc, output_should_stop_mpc + for idx in range(2): accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05) self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1]) diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py index 7bcf1a148a..e6001f2642 100644 --- a/sunnypilot/models/helpers.py +++ b/sunnypilot/models/helpers.py @@ -19,7 +19,7 @@ from openpilot.system.hardware import PC from openpilot.system.hardware.hw import Paths from pathlib import Path -CURRENT_SELECTOR_VERSION = 2 +CURRENT_SELECTOR_VERSION = 3 REQUIRED_MIN_SELECTOR_VERSION = 2 USE_ONNX = os.getenv('USE_ONNX', PC) diff --git a/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py b/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py index 56f32373d5..9f0c503fb4 100644 --- a/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py +++ b/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py @@ -7,6 +7,7 @@ See the LICENSE.md file in the root directory for more details. from cereal import messaging, custom from opendbc.car import structs +from openpilot.sunnypilot.models.helpers import get_active_model_runner from openpilot.sunnypilot.selfdrive.controls.lib.dec.dec import DynamicExperimentalController DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimentalControlState @@ -15,6 +16,7 @@ DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimen class LongitudinalPlannerSP: def __init__(self, CP: structs.CarParams, mpc): self.dec = DynamicExperimentalController(CP, mpc) + self.is_stock = get_active_model_runner() == custom.ModelManagerSP.Runner.stock def get_mpc_mode(self) -> str | None: if not self.dec.active(): From 908ea361264a85e8feda127133023996cce75bb3 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 9 May 2025 23:23:37 +0800 Subject: [PATCH 150/477] ui(raylib): add thread-safe property access to WifiManagerWrapper (#35162) * Add thread-safe property access to WifiManagerWrapper * cleanup * type-safe manager, always specify default * import --------- Co-authored-by: Cameron Clough --- system/ui/lib/wifi_manager.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index af533e9a2a..990e1d14a1 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -1,10 +1,12 @@ import asyncio +import concurrent.futures import threading import time import uuid from collections.abc import Callable from dataclasses import dataclass from enum import IntEnum +from typing import TypeVar from dbus_next.aio import MessageBus from dbus_next import BusType, Variant, Message @@ -12,6 +14,8 @@ from dbus_next.errors import DBusError from dbus_next.constants import MessageType from openpilot.common.swaglog import cloudlog +T = TypeVar("T") + # NetworkManager constants NM = "org.freedesktop.NetworkManager" NM_PATH = '/org/freedesktop/NetworkManager' @@ -437,11 +441,11 @@ class WifiManagerWrapper: @property def networks(self) -> list[NetworkInfo]: """Get the current list of networks.""" - return self._manager.networks if self._manager else [] + return self._run_coroutine_sync(lambda manager: manager.networks.copy(), default=[]) def is_saved(self, ssid: str) -> bool: """Check if a network is saved.""" - return self._manager.is_saved(ssid) if self._manager else False + return self._run_coroutine_sync(lambda manager: manager.is_saved(ssid), default=False) def connect(self): """Connect to DBus and start Wi-Fi scanning.""" @@ -479,3 +483,22 @@ class WifiManagerWrapper: cloudlog.error("WifiManager thread is not running") return asyncio.run_coroutine_threadsafe(coro, self._loop) + + def _run_coroutine_sync(self, func: Callable[[WifiManager], T], default: T) -> T: + """Run a function synchronously in the async thread.""" + if not self._running or not self._loop or not self._manager: + return default + future = concurrent.futures.Future[T]() + + def wrapper(manager: WifiManager) -> None: + try: + future.set_result(func(manager)) + except Exception as e: + future.set_exception(e) + + try: + self._loop.call_soon_threadsafe(wrapper, self._manager) + return future.result(timeout=1.0) + except Exception as e: + cloudlog.error(f"WifiManagerWrapper property access failed: {e}") + return default From 179da5d0075ad61cd2ce0dbd55c2d9f6a8106790 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 12:57:58 -0400 Subject: [PATCH 151/477] Update Python packages (#893) --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 95fc304727..36461557c4 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 95fc304727483d0d7f18f7df348d941046e046f2 +Subproject commit 36461557c47176b16a081ea40195bc327a34d97f From 68c593db5f1c9677eee71dd2f7b66dc7f6e02ba0 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 14:53:56 -0400 Subject: [PATCH 152/477] Revert "Hyundai: custom longitudinal tuning" (#892) * Revert "Hyundai: custom longitudinal tuning (#658)" This reverts commit 72f09ec9f5a8bd8695c9dc50bd5fe9bfccc3d9b1. * bump * Revert "bump" This reverts commit a437f3538db4720d797e3af7df8363898d410a7e. * bump --- common/params_keys.h | 1 - selfdrive/ui/sunnypilot/SConscript | 1 - .../settings/vehicle/hyundai_settings.cc | 84 ------------------- .../settings/vehicle/hyundai_settings.h | 32 ------- .../qt/offroad/settings/vehicle_panel.cc | 25 ------ .../qt/offroad/settings/vehicle_panel.h | 14 +--- sunnypilot/selfdrive/car/interfaces.py | 19 +---- system/manager/manager.py | 1 - 8 files changed, 3 insertions(+), 174 deletions(-) delete mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc delete mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h diff --git a/common/params_keys.h b/common/params_keys.h index 97f23d5108..575dcc2adc 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -164,7 +164,6 @@ inline static std::unordered_map keys = { {"BackupManager_RestoreVersion", PERSISTENT}, // sunnypilot car specific params - {"HyundaiLongitudinalTuning", PERSISTENT}, {"HyundaiRadarTracks", PERSISTENT}, {"HyundaiRadarTracksConfirmed", PERSISTENT}, {"HyundaiRadarTracksPersistent", PERSISTENT}, diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 82a257523e..4ff3f70477 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -50,7 +50,6 @@ network_src = [ ] vehicle_panel_qt_src = [ - "sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc", "sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc", ] diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc deleted file mode 100644 index 08d76f5927..0000000000 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" - -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - -HyundaiSettings::HyundaiSettings(QWidget *parent) : QWidget(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - std::vector tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") }; - longitudinalTuningToggle = new ButtonParamControlSP( - "HyundaiLongitudinalTuning", - tr("Custom Longitudinal Tuning"), - tr("Select a tuning mode.\n" - "Off: no custom tuning applied.\n" - "Dynamic: on-the-spot adjustments using dynamic calculations.\n" - "Predictive: adjusts based on anticipated ACC variation."), - "", - tuning_texts, - 500 - ); - longitudinalTuningToggle->showDescription(); - longitudinalTuningToggle->setProperty("originalDesc", longitudinalTuningToggle->getDescription()); - list->addItem(longitudinalTuningToggle); - - QObject::connect(uiState(), &UIState::offroadTransition, this, &HyundaiSettings::updateSettings); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -QString HyundaiSettings::toggleDisableMsg() const { - if (!has_longitudinal_control) { - return tr("This feature can only be used with openpilot longitudinal control enabled."); - } - - if (!offroad) { - return tr("Enable \"Always Offroad\" in Device panel, or turn vehicle off to select an option."); - } - - return QString(); -} - -void HyundaiSettings::showEvent(QShowEvent *event) { - updateSettings(offroad); -} - -void HyundaiSettings::updateSettings(bool _offroad) { - if (!isVisible()) { - return; - } - - auto cp_bytes = params.get("CarParamsPersistent"); - if (!cp_bytes.empty()) { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); - cereal::CarParams::Reader CP = cmsg.getRoot(); - - has_longitudinal_control = hasLongitudinalControl(CP); - - QString longitudinal_tuning_disabled_msg = toggleDisableMsg(); - if (!longitudinal_tuning_disabled_msg.isEmpty()) { - longitudinalTuningToggle->setEnabled(false); - longitudinalTuningToggle->setDescription(longitudinal_tuning_disabled_msg); - } else { - longitudinalTuningToggle->setEnabled(true); - longitudinalTuningToggle->setDescription(longitudinalTuningToggle->property("originalDesc").toString()); - } - - longitudinalTuningToggle->showDescription(); - } else { - has_longitudinal_control = false; - longitudinalTuningToggle->setEnabled(false); - } - - offroad = _offroad; -} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h deleted file mode 100644 index 3137a0d1b9..0000000000 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - * - * This file is part of sunnypilot and is licensed under the MIT License. - * See the LICENSE.md file in the root directory for more details. - */ - -#pragma once - -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/sunnypilot/ui.h" -#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" - -class HyundaiSettings : public QWidget { - Q_OBJECT - -public: - explicit HyundaiSettings(QWidget *parent = nullptr); - void showEvent(QShowEvent *event) override; - -public slots: - void updateSettings(bool _offroad); - -private: - Params params; - bool offroad = false; - bool has_longitudinal_control = false; - - ButtonParamControlSP *longitudinalTuningToggle = nullptr; - QString toggleDisableMsg() const; -}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc index bc9f645f3b..cc3e6df183 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc @@ -7,7 +7,6 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" -#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { @@ -19,16 +18,11 @@ VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { vlayout->setContentsMargins(50, 20, 50, 20); platformSelector = new PlatformSelector(); - QObject::connect(platformSelector, &PlatformSelector::refreshPanel, this, &VehiclePanel::updateBrandSettings); list->addItem(platformSelector); ScrollViewSP *scroller = new ScrollViewSP(list, this); vlayout->addWidget(scroller); - hyundaiSettings = new HyundaiSettings(this); - vlayout->addWidget(hyundaiSettings); - hyundaiSettings->setVisible(false); - QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel); main_layout->addWidget(vehicleScreen); @@ -42,24 +36,5 @@ void VehiclePanel::showEvent(QShowEvent *event) { void VehiclePanel::updatePanel(bool _offroad) { platformSelector->refresh(_offroad); - updateBrandSettings(); - offroad = _offroad; } - -void VehiclePanel::updateBrandSettings() { - if (!isVisible()) { - return; - } - - resetBrandSettings(); - - QString brand = platformSelector->getPlatformBundle("brand").toString(); - if (brand == "hyundai") { - hyundaiSettings->setVisible(true); - } -} - -void VehiclePanel::resetBrandSettings() { - hyundaiSettings->setVisible(false); -} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h index 5548ff7181..f6265da7a6 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h @@ -9,7 +9,6 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" -#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h" class VehiclePanel : public QFrame { @@ -23,17 +22,8 @@ public slots: void updatePanel(bool _offroad); private: - void resetBrandSettings(); - QStackedLayout* main_layout = nullptr; QWidget* vehicleScreen = nullptr; - PlatformSelector* platformSelector = nullptr; - - // brand panels - HyundaiSettings* hyundaiSettings = nullptr; - - bool offroad = false; - -private slots: - void updateBrandSettings(); + PlatformSelector *platformSelector = nullptr; + bool offroad; }; diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index f2019f5ed7..2c27e960e9 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -11,7 +11,6 @@ from opendbc.car.car_helpers import can_fingerprint from opendbc.car.interfaces import CarInterfaceBase from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC -from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog @@ -26,21 +25,6 @@ def log_fingerprint(CP: structs.CarParams) -> None: else: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) -def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, - params: Params = None) -> None: - if params is None: - params = Params() - - # Hyundai Custom Longitudinal Tuning - if CP.brand == 'hyundai': - hyundai_longitudinal_tuning = int(params.get("HyundaiLongitudinalTuning", encoding="utf8") or 0) - if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC: - CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value - if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE: - CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value - - CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP) - def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None, enabled: bool = False) -> None: @@ -77,11 +61,10 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, CP.radarUnavailable = False -def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None: +def setup_interfaces(CI: CarInterfaceBase, params: Params = None): CP = CI.CP CP_SP = CI.CP_SP - _initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params) _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) _initialize_radar_tracks(CP, CP_SP, params) diff --git a/system/manager/manager.py b/system/manager/manager.py index 90df415a8c..fde85f0f7c 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -45,7 +45,6 @@ def manager_init() -> None: ("AutoLaneChangeTimer", "0"), ("AutoLaneChangeBsmDelay", "0"), ("DynamicExperimentalControl", "0"), - ("HyundaiLongitudinalTuning", "0"), ("Mads", "1"), ("MadsMainCruiseAllowed", "1"), ("MadsPauseLateralOnBrake", "0"), From 1f312e16d492473a4d252c603f04ec7aff71cfd4 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Fri, 9 May 2025 15:13:52 -0400 Subject: [PATCH 153/477] messaging: cleanup zero-frequency service initialization (#35145) * messaging: fix bug with relaxed checks under simulation * refactor * cleanup * fix bug * Revert "fix bug" This reverts commit ea31f3ee83676c5b8ffe57500881557f7586998c. * that did need to be different * fix bug, add test coverage * retry CI --- cereal/messaging/__init__.py | 26 +++++++++---------- cereal/messaging/tests/test_pub_sub_master.py | 6 ++++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cereal/messaging/__init__.py b/cereal/messaging/__init__.py index 8ad956b61b..b03285f80a 100644 --- a/cereal/messaging/__init__.py +++ b/cereal/messaging/__init__.py @@ -145,12 +145,16 @@ class SubMaster: self.updated = {s: False for s in services} self.recv_time = {s: 0. for s in services} self.recv_frame = {s: 0 for s in services} - self.alive = {s: False for s in services} - self.freq_ok = {s: False for s in services} self.sock = {} self.data = {} - self.valid = {} - self.logMonoTime = {} + self.logMonoTime = {s: 0 for s in services} + + # zero-frequency / on-demand services are always alive and presumed valid; all others must pass checks + on_demand = {s: SERVICE_LIST[s].frequency <= 1e-5 for s in services} + self.static_freq_services = set(s for s in services if not on_demand[s]) + self.alive = {s: on_demand[s] for s in services} + self.freq_ok = {s: on_demand[s] for s in services} + self.valid = {s: on_demand[s] for s in services} self.freq_tracker: Dict[str, FrequencyTracker] = {} self.poller = Poller() @@ -177,8 +181,6 @@ class SubMaster: data = new_message(s, 0) # lists self.data[s] = getattr(data.as_reader(), s) - self.logMonoTime[s] = 0 - self.valid[s] = False self.freq_tracker[s] = FrequencyTracker(SERVICE_LIST[s].frequency, self.update_freq, s == poll) def __getitem__(self, s: str) -> capnp.lib.capnp._DynamicStructReader: @@ -215,14 +217,10 @@ class SubMaster: self.logMonoTime[s] = msg.logMonoTime self.valid[s] = msg.valid - for s in self.services: - if SERVICE_LIST[s].frequency > 1e-5 and not self.simulation: - # alive if delay is within 10x the expected frequency - self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) - self.freq_ok[s] = self.freq_tracker[s].valid - else: - self.freq_ok[s] = True - self.alive[s] = self.seen[s] if self.simulation else True + for s in self.static_freq_services: + # alive if delay is within 10x the expected frequency; checks relaxed in simulator + self.alive[s] = (cur_time - self.recv_time[s]) < (10. / SERVICE_LIST[s].frequency) or (self.seen[s] and self.simulation) + self.freq_ok[s] = self.freq_tracker[s].valid or self.simulation def all_alive(self, service_list: Optional[List[str]] = None) -> bool: return all(self.alive[s] for s in (service_list or self.services) if s not in self.ignore_alive) diff --git a/cereal/messaging/tests/test_pub_sub_master.py b/cereal/messaging/tests/test_pub_sub_master.py index e9bc7a85cb..e47e713393 100644 --- a/cereal/messaging/tests/test_pub_sub_master.py +++ b/cereal/messaging/tests/test_pub_sub_master.py @@ -6,6 +6,7 @@ import cereal.messaging as messaging from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \ random_bytes, random_carstate, assert_carstate, \ zmq_sleep +from cereal.services import SERVICE_LIST class TestSubMaster: @@ -26,7 +27,9 @@ class TestSubMaster: sm = messaging.SubMaster(socks) assert sm.frame == -1 assert not any(sm.updated.values()) - assert not any(sm.alive.values()) + assert not any(sm.seen.values()) + on_demand = {s: SERVICE_LIST[s].frequency <= 1e-5 for s in sm.services} + assert all(sm.alive[s] == sm.valid[s] == sm.freq_ok[s] == on_demand[s] for s in sm.services) assert all(t == 0. for t in sm.recv_time.values()) assert all(f == 0 for f in sm.recv_frame.values()) assert all(t == 0 for t in sm.logMonoTime.values()) @@ -83,6 +86,7 @@ class TestSubMaster: "cameraOdometry": (20, 10), "liveCalibration": (4, 4), "carParams": (None, None), + "userFlag": (None, None), } for service, (max_freq, min_freq) in checks.items(): From 32167e02c0f9a3d6910061d453bfb0c5a63add2d Mon Sep 17 00:00:00 2001 From: David <49467229+TheSecurityDev@users.noreply.github.com> Date: Fri, 9 May 2025 14:57:00 -0500 Subject: [PATCH 154/477] ui: show alert when user flag (bookmark) pressed (#34920) * feat: Show alert on user flag event * feat: Temporarily update border status on user flag to match connect timeline * Revert "feat: Temporarily update border status on user flag to match connect timeline" This reverts commit f1da6a4f5f555d4db50b47fb68ce74ae7737b8af. * feat: Increase alert duration to 1.5 seconds * remove audible alert for bookmark event * refactor: Use NormalPermamentAlert for user flag alert * fix: Update userFlag enum value in OnroadEvent struct * fix: Handle userFlag event even in dashcam mode * don't need to ignore that anymore * remove 'userFlag' from ignore list --------- Co-authored-by: Jason Young --- cereal/log.capnp | 1 + selfdrive/selfdrived/events.py | 5 ++++- selfdrive/selfdrived/selfdrived.py | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cereal/log.capnp b/cereal/log.capnp index 6b5398405c..5c5ceb5635 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -127,6 +127,7 @@ struct OnroadEvent @0xc4fa6047f024e718 { espActive @90; personalityChanged @91; aeb @92; + userFlag @95; soundsUnavailableDEPRECATED @47; } diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index c187ed474a..6293deb2af 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -894,7 +894,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # causing the connection to the panda to be lost EventName.usbError: { ET.SOFT_DISABLE: soft_disable_alert("USB Error: Reboot Your Device"), - ET.PERMANENT: NormalPermanentAlert("USB Error: Reboot Your Device", ""), + ET.PERMANENT: NormalPermanentAlert("USB Error: Reboot Your Device"), ET.NO_ENTRY: NoEntryAlert("USB Error: Reboot Your Device"), }, @@ -982,6 +982,9 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { ET.WARNING: personality_changed_alert, }, + EventName.userFlag: { + ET.PERMANENT: NormalPermanentAlert("Bookmark Saved", duration=1.5), + }, } diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index face7dab29..c888551777 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -77,7 +77,7 @@ class SelfdriveD: self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', - 'controlsState', 'carControl', 'driverAssistance', 'alertDebug'] + \ + 'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userFlag'] + \ self.camera_packets + self.sensor_packets + self.gps_packets, ignore_alive=ignore, ignore_avg_freq=ignore, ignore_valid=ignore, frequency=int(1/DT_CTRL)) @@ -159,7 +159,11 @@ class SelfdriveD: self.events.add(EventName.selfdriveInitializing) return - # no more events while in dashcam mode + # Check for user flag (bookmark) press + if self.sm.updated['userFlag']: + self.events.add(EventName.userFlag) + + # Don't add any more events while in dashcam mode if self.CP.passive: return @@ -365,7 +369,7 @@ class SelfdriveD: if self.sm['modelV2'].frameDropPerc > 20: self.events.add(EventName.modeldLagging) - # decrement personality on distance button press + # Decrement personality on distance button press if self.CP.openpilotLongitudinalControl: if any(not be.pressed and be.type == ButtonType.gapAdjustCruise for be in CS.buttonEvents): self.personality = (self.personality - 1) % 3 From 80f21949a31861a3d9c4467caa7a856a125d83a9 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 17:42:31 -0400 Subject: [PATCH 155/477] ui: Adjust stretch factor for `ListWidgetSP` (#896) --- selfdrive/ui/sunnypilot/qt/widgets/controls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index d1edaf8621..0565690b20 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -360,7 +360,7 @@ public: outer_layout.addLayout(&inner_layout); inner_layout.setMargin(0); inner_layout.setSpacing(25); // default spacing is 25 - outer_layout.addStretch(); + outer_layout.addStretch(1); } inline void addItem(QWidget *w) { inner_layout.addWidget(w); } inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); } From db855d56d307d0eb935e96c87254771955ed86b0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 9 May 2025 15:18:52 -0700 Subject: [PATCH 156/477] Log Hexagon DSP temperature (#35166) * add hexagon dsp temp * log * whoops --- cereal/log.capnp | 1 + system/hardware/base.py | 1 + system/hardware/tici/hardware.py | 1 + 3 files changed, 3 insertions(+) diff --git a/cereal/log.capnp b/cereal/log.capnp index 5c5ceb5635..9ab51e0b77 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -490,6 +490,7 @@ struct DeviceState @0xa4d8b5af2aa492eb { # device thermals cpuTempC @26 :List(Float32); gpuTempC @27 :List(Float32); + dspTempC @49 :Float32; memoryTempC @28 :Float32; nvmeTempC @35 :List(Float32); modemTempC @36 :List(Float32); diff --git a/system/hardware/base.py b/system/hardware/base.py index 1e3b94e44e..90b42b2f1f 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -33,6 +33,7 @@ class ThermalZone: class ThermalConfig: cpu: list[ThermalZone] | None = None gpu: list[ThermalZone] | None = None + dsp: ThermalZone | None = None pmic: list[ThermalZone] | None = None memory: ThermalZone | None = None intake: ThermalZone | None = None diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index ffa852403f..694d42f4d6 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -335,6 +335,7 @@ class Tici(HardwareBase): return ThermalConfig(cpu=[ThermalZone(f"cpu{i}-silver-usr") for i in range(4)] + [ThermalZone(f"cpu{i}-gold-usr") for i in range(4)], gpu=[ThermalZone("gpu0-usr"), ThermalZone("gpu1-usr")], + dsp=ThermalZone("compute-hvx-usr"), memory=ThermalZone("ddr-usr"), pmic=[ThermalZone("pm8998_tz"), ThermalZone("pm8005_tz")], intake=intake, From ca23bb90cd509c3da0ae861e01e79be17415deb6 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 18:29:33 -0400 Subject: [PATCH 157/477] ui: Platform Selector updates (#897) * init * more * description * init them to true * back to false * fix * add description update colors fix stretch * yellow & orange are too similar - use blue * slight updates * dynamic description * split it out * more --------- Co-authored-by: nayan8teen --- .../settings/vehicle/platform_selector.cc | 49 ++++++++++++++++--- .../settings/vehicle/platform_selector.h | 36 ++++++++++++++ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc index 83fa0b1698..21c9a44cb4 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc @@ -43,37 +43,74 @@ PlatformSelector::PlatformSelector() : ButtonControl(tr("Vehicle"), "", "") { } }); + main_layout->addStretch(0); refresh(offroad); } void PlatformSelector::refresh(bool _offroad) { QString name = getPlatformBundle("name").toString(); + platform = unrecognized_str; + QString platform_color = YELLOW_PLATFORM; + if (!name.isEmpty()) { - setValue(name); + platform = name; + platform_color = BLUE_PLATFORM; + brand = getPlatformBundle("brand").toString(); setText(tr("REMOVE")); } else { setText(tr("SEARCH")); + + platform = unrecognized_str; + brand = ""; auto cp_bytes = params.get("CarParamsPersistent"); if (!cp_bytes.empty()) { AlignedBuffer aligned_buf; capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); cereal::CarParams::Reader CP = cmsg.getRoot(); - setValue(QString::fromStdString(CP.getCarFingerprint().cStr())); + + platform = QString::fromStdString(CP.getCarFingerprint().cStr()); + + for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) { + if (it.value()["platform"].toString() == platform) { + platform = it.key(); + brand = it.value()["brand"].toString(); + break; + } + } + + if (platform == "MOCK") { + platform = unrecognized_str; + } else { + platform_color = GREEN_PLATFORM; + } } } + setValue(platform, platform_color); setEnabled(true); emit refreshPanel(); offroad = _offroad; + + FingerprintStatus cur_status; + if (platform_color == GREEN_PLATFORM) { + cur_status = FingerprintStatus::AUTO_FINGERPRINT; + } else if (platform_color == BLUE_PLATFORM) { + cur_status = FingerprintStatus::MANUAL_FINGERPRINT; + } else { + cur_status = FingerprintStatus::UNRECOGNIZED; + } + + setDescription(platformDescription(cur_status)); + showDescription(); } -void PlatformSelector::setPlatform(const QString &platform) { - QVariantMap platform_data = platforms[platform]; +void PlatformSelector::setPlatform(const QString &_platform) { + QVariantMap platform_data = platforms[_platform]; const QString offroad_msg = offroad ? tr("This setting will take effect immediately.") : tr("This setting will take effect once the device enters offroad state."); const QString msg = QString("%1

%2") - .arg(platform, offroad_msg); + .arg(_platform, offroad_msg); QString content("

" + tr("Vehicle Selector") + "


" "

" + msg + "

"); @@ -81,7 +118,7 @@ void PlatformSelector::setPlatform(const QString &platform) { if (ConfirmationDialog(content, tr("Confirm"), tr("Cancel"), true, this).exec()) { QJsonObject json_bundle; json_bundle["platform"] = platform_data["platform"].toString(); - json_bundle["name"] = platform; + json_bundle["name"] = _platform; json_bundle["make"] = platform_data["make"].toString(); json_bundle["brand"] = platform_data["brand"].toString(); json_bundle["model"] = platform_data["model"].toString(); diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h index 3d912f822c..9867371ef6 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h @@ -9,6 +9,16 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +static const QString GREEN_PLATFORM = "#00F100"; +static const QString BLUE_PLATFORM = "#0086E9"; +static const QString YELLOW_PLATFORM = "#FFD500"; + +enum class FingerprintStatus { + AUTO_FINGERPRINT, + MANUAL_FINGERPRINT, + UNRECOGNIZED, +}; + class PlatformSelector : public ButtonControl { Q_OBJECT @@ -16,6 +26,9 @@ public: PlatformSelector(); QVariant getPlatformBundle(const QString &key); + QString platform; + QString brand; + public slots: void refresh(bool _offroad); @@ -29,4 +42,27 @@ private: Params params; bool offroad; + + QString unrecognized_str = tr("Unrecognized Vehicle"); + + static QString platformDescription(FingerprintStatus status = FingerprintStatus::UNRECOGNIZED) { + QString auto_str = "🟢 - " + tr("Fingerprinted automatically"); + QString manual_str = "🔵 - " + tr("Manually selected"); + QString unrecognized_str = "🟡 - " + tr("Not fingerprinted or manually selected"); + + if (status == FingerprintStatus::AUTO_FINGERPRINT) { + auto_str = "" + auto_str + ""; + } else if (status == FingerprintStatus::MANUAL_FINGERPRINT) { + manual_str = "" + manual_str + ""; + } else { + unrecognized_str = "" + unrecognized_str + ""; + } + + return QString("%1
%2

%3
%4
%5") + .arg(tr("Select vehicle to force fingerprint manually.")) + .arg(tr("Colors represent fingerprint status:")) + .arg(auto_str) + .arg(manual_str) + .arg(unrecognized_str); + } }; From e89d65b51644bd86c2f09f0a2298c4f65c36a497 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 22:08:32 -0400 Subject: [PATCH 158/477] ui: fix screens for UI Preview (#899) * ui: fix screens for UI Preview * try a delay --- selfdrive/ui/tests/test_ui/run.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 064ef14a73..9d078fdd9e 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -37,7 +37,7 @@ DATA: dict[str, capnp.lib.capnp._DynamicStructBuilder] = dict.fromkeys( "driverStateV2", "roadCameraState", "wideRoadCameraState", "driverCameraState"], None) def setup_homescreen(click, pm: PubMaster, scroll=None): - pass + time.sleep(UI_DELAY) def setup_settings_device(click, pm: PubMaster, scroll=None): click(100, 100) @@ -61,6 +61,7 @@ def setup_settings_software(click, pm: PubMaster, scroll=None): time.sleep(UI_DELAY) def setup_settings_firehose(click, pm: PubMaster, scroll=None): + setup_settings_device(click, pm) scroll(-400, 278, 962) click(278, 862) @@ -151,7 +152,7 @@ def setup_keyboard_uppercase(click, pm: PubMaster, scroll=None): def setup_driver_camera(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) - click(1720, 620) + click(1720, 825) DATA['deviceState'].deviceState.started = False setup_onroad(click, pm) DATA['deviceState'].deviceState.started = True From e5ac7d5b571b93b7caf03fbb293eed935d59e0eb Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 9 May 2025 22:19:14 -0400 Subject: [PATCH 159/477] UI: Support inline layout for `ButtonParamControlSP` (#888) * support inline layout for ButtonParamControlSP * final --------- Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/qt/widgets/controls.h | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index 0565690b20..21c317eac6 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -219,7 +219,7 @@ class ButtonParamControlSP : public AbstractControlSP_SELECTOR { public: ButtonParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const std::vector &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) { + const std::vector &button_texts, const int minimum_button_width = 300, const bool inline_layout = false) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts), is_inline_layout(inline_layout) { const QString style = R"( QPushButton { border-radius: 20px; @@ -246,6 +246,19 @@ public: key = param.toStdString(); int value = atoi(params.get(key).c_str()); + if (inline_layout) { + button_param_layout->setMargin(0); + button_param_layout->setSpacing(0); + if (!title.isEmpty()) { + main_layout->removeWidget(title_label); + hlayout->addWidget(title_label, 1); + } + if (spacingItem != nullptr && main_layout->indexOf(spacingItem) != -1) { + main_layout->removeItem(spacingItem); + spacingItem = nullptr; + } + } + button_group = new QButtonGroup(this); button_group->setExclusive(true); for (int i = 0; i < button_texts.size(); i++) { @@ -254,12 +267,18 @@ public: button->setChecked(i == value); button->setStyleSheet(style); button->setMinimumWidth(minimum_button_width); - if (i == 0) hlayout->addSpacing(2); - hlayout->addWidget(button); + if (i == 0) button_param_layout->addSpacing(2); + button_param_layout->addWidget(button); button_group->addButton(button, i); } - hlayout->setAlignment(Qt::AlignLeft); + button_param_layout->setAlignment(Qt::AlignLeft); + if (is_inline_layout) { + QFrame *container = new QFrame; + container->setLayout(button_param_layout); + container->setStyleSheet("background-color: #393939; border-radius: 20px;"); + hlayout->addWidget(container); + } QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), [=](int id) { params.put(key, std::to_string(id)); @@ -314,6 +333,10 @@ public: protected: void paintEvent(QPaintEvent *event) override { + if (is_inline_layout) { + return; + } + QPainter p(this); p.setRenderHint(QPainter::Antialiasing); @@ -348,6 +371,8 @@ private: std::vector button_texts; bool button_group_enabled = true; + bool is_inline_layout; + QHBoxLayout *button_param_layout = is_inline_layout ? new QHBoxLayout() : hlayout; }; class ListWidgetSP : public QWidget { @@ -416,8 +441,8 @@ class OptionControlSP : public AbstractControlSP_SELECTOR { Q_OBJECT private: - bool isInlineLayout; - QHBoxLayout *optionSelectorLayout = isInlineLayout ? new QHBoxLayout() : hlayout; + bool is_inline_layout; + QHBoxLayout *optionSelectorLayout = is_inline_layout ? new QHBoxLayout() : hlayout; struct MinMaxValue { int min_value; @@ -438,7 +463,7 @@ private: public: OptionControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon, - const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), isInlineLayout(inline_layout) { + const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), is_inline_layout(inline_layout) { const QString style = R"( QPushButton { border-radius: 20px; @@ -513,7 +538,7 @@ public: } optionSelectorLayout->setAlignment(Qt::AlignLeft); - if (isInlineLayout) { + if (is_inline_layout) { QFrame *container = new QFrame; container->setLayout(optionSelectorLayout); container->setStyleSheet("background-color: #393939; border-radius: 20px;"); @@ -540,7 +565,7 @@ public: protected: void paintEvent(QPaintEvent *event) override { - if (isInlineLayout) { + if (is_inline_layout) { return; } From f4c7b04682fca619d630546903949764093106d9 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 22:40:23 -0400 Subject: [PATCH 160/477] ui: encapsulate always enabled buttons into a list in Device panel (#900) * fix style handling * vector them * another pr --------- Co-authored-by: nayan8teen --- .../ui/sunnypilot/qt/offroad/settings/device_panel.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc index 3e613e1022..70ead6e282 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc @@ -112,9 +112,17 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) { addItem(power_group_layout); + std::vector always_enabled_btns = { + rebootBtn, + poweroffBtn, + offroadBtn, + }; + QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { for (auto btn : findChildren()) { - if (btn != rebootBtn && btn != poweroffBtn && btn != offroadBtn) { + bool always_enabled = std::find(always_enabled_btns.begin(), always_enabled_btns.end(), btn) != always_enabled_btns.end(); + + if (!always_enabled) { btn->setEnabled(offroad); } } From 897a2bcedce0e4b88cab91b965c4dad1a7353242 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 9 May 2025 22:50:52 -0400 Subject: [PATCH 161/477] ui: update Quiet Mode button behavior (#880) * fix style handling * vector them --------- Co-authored-by: Jason Wen --- .../qt/offroad/settings/device_panel.cc | 1 + selfdrive/ui/sunnypilot/qt/widgets/controls.h | 19 +++++-------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc index 70ead6e282..e6855c4b4e 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc @@ -116,6 +116,7 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) { rebootBtn, poweroffBtn, offroadBtn, + buttons["quietModeBtn"], }; QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index 21c317eac6..2a1bc20ac1 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -658,16 +658,6 @@ public: } } -protected: - // Override mouse release event to handle style updates smoothly - void mouseReleaseEvent(QMouseEvent *event) override { - if (!key.empty()) { - bool next_state = !params.getBool(key); - updateStyle(next_state); - } - QPushButton::mouseReleaseEvent(event); - } - private: std::string key = ""; Params params; @@ -676,13 +666,14 @@ private: QString btn_enabled_off_style = "QPushButton:enabled { background-color: #393939; }"; QString btn_enabled_on_style = "QPushButton:enabled { background-color: #1e79e8; }"; - QString btn_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }"; - QString btn_disabled_stype = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }"; + QString btn_off_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }"; + QString btn_on_pressed_style = "QPushButton:pressed { background-color: #1E8FFF; }"; + QString btn_disabled_style = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }"; void updateStyle(bool enabled) { QString enabled_style = enabled ? btn_enabled_on_style : btn_enabled_off_style; - - setStyleSheet(buttonStyle + enabled_style + btn_pressed_style + btn_disabled_stype); + QString pressed_style = enabled ? btn_on_pressed_style : btn_off_pressed_style; + setStyleSheet(buttonStyle + enabled_style + pressed_style + btn_disabled_style); } }; From 13965bdf0bda5ad1ef0b7faa6011bc8e3274fc17 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Fri, 9 May 2025 19:59:44 -0700 Subject: [PATCH 162/477] docs: clarify process to connect to 3/3X over ADB (#35090) * three * codespell being smart with me --------- Co-authored-by: Maxime Desroches --- docs/assets/three-back.svg | 3 +++ docs/how-to/connect-to-comma.md | 6 +++++- pyproject.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 docs/assets/three-back.svg diff --git a/docs/assets/three-back.svg b/docs/assets/three-back.svg new file mode 100644 index 0000000000..4dfeeeb46a --- /dev/null +++ b/docs/assets/three-back.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a5245f9458982b608fee67fc689d899ca638a405ff62bf9db5e4978b177ef3e +size 121394 diff --git a/docs/how-to/connect-to-comma.md b/docs/how-to/connect-to-comma.md index 469ef81672..cbaccaae6a 100644 --- a/docs/how-to/connect-to-comma.md +++ b/docs/how-to/connect-to-comma.md @@ -32,9 +32,13 @@ For doing development work on device, it's recommended to use [SSH agent forward ## ADB -In order to use ADB on your device, you'll need to enable it in the device's settings. +In order to use ADB on your device, you'll need to perform the following steps using the image below for reference: +![comma 3/3x back](../assets/three-back.svg) + +* Plug your device into constant power using port 2, letting the device boot up * Enable ADB in your device's settings +* Plug in your device to your PC using port 1 * Connect to your device * `adb shell` over USB * `adb connect` over WiFi diff --git a/pyproject.toml b/pyproject.toml index bf43840d71..20bfebd938 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -171,7 +171,7 @@ quiet-level = 3 # if you've got a short variable name that's getting flagged, add it here ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl" builtin = "clear,rare,informal,code,names,en-GB_to_en-US" -skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*" +skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*" [tool.mypy] python_version = "3.11" From fb934247a28a439de23735024ec8074587c0c416 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 9 May 2025 23:09:27 -0400 Subject: [PATCH 163/477] ui: brand settings for Vehicle panel (#898) * init * brand factory * more * mazda * nissan * rivian * subaru * tesla * toyota * volkswagen * do this * stretch here --- selfdrive/ui/sunnypilot/SConscript | 19 +++++- .../vehicle/brand_settings_factory.cc | 62 +++++++++++++++++++ .../settings/vehicle/brand_settings_factory.h | 18 ++++++ .../vehicle/brand_settings_interface.cc | 8 +++ .../vehicle/brand_settings_interface.h | 21 +++++++ .../qt/offroad/settings/vehicle/brands.h | 21 +++++++ .../settings/vehicle/chrysler_settings.cc | 31 ++++++++++ .../settings/vehicle/chrysler_settings.h | 27 ++++++++ .../offroad/settings/vehicle/ford_settings.cc | 31 ++++++++++ .../offroad/settings/vehicle/ford_settings.h | 27 ++++++++ .../offroad/settings/vehicle/gm_settings.cc | 31 ++++++++++ .../qt/offroad/settings/vehicle/gm_settings.h | 27 ++++++++ .../settings/vehicle/honda_settings.cc | 31 ++++++++++ .../offroad/settings/vehicle/honda_settings.h | 27 ++++++++ .../settings/vehicle/hyundai_settings.cc | 31 ++++++++++ .../settings/vehicle/hyundai_settings.h | 27 ++++++++ .../settings/vehicle/mazda_settings.cc | 31 ++++++++++ .../offroad/settings/vehicle/mazda_settings.h | 27 ++++++++ .../settings/vehicle/nissan_settings.cc | 31 ++++++++++ .../settings/vehicle/nissan_settings.h | 27 ++++++++ .../settings/vehicle/rivian_settings.cc | 31 ++++++++++ .../settings/vehicle/rivian_settings.h | 27 ++++++++ .../settings/vehicle/subaru_settings.cc | 31 ++++++++++ .../settings/vehicle/subaru_settings.h | 27 ++++++++ .../settings/vehicle/tesla_settings.cc | 31 ++++++++++ .../offroad/settings/vehicle/tesla_settings.h | 27 ++++++++ .../settings/vehicle/toyota_settings.cc | 31 ++++++++++ .../settings/vehicle/toyota_settings.h | 27 ++++++++ .../settings/vehicle/volkswagen_settings.cc | 31 ++++++++++ .../settings/vehicle/volkswagen_settings.h | 27 ++++++++ .../qt/offroad/settings/vehicle_panel.cc | 27 +++++++- .../qt/offroad/settings/vehicle_panel.h | 9 ++- 32 files changed, 877 insertions(+), 4 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 4ff3f70477..379adfcede 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -50,11 +50,28 @@ network_src = [ ] vehicle_panel_qt_src = [ + "sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc", + "sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc", "sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc", ] +brand_settings_qt_src = [ + "sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc", + "sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc", +] + sp_widgets_src = widgets_src + network_src -sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src +sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src + brand_settings_qt_src sp_qt_util = qt_util Export('sp_widgets_src', 'sp_qt_src', "sp_qt_util") diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc new file mode 100644 index 0000000000..08b137d270 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h" + +static const QStringList supportedBrands = { + "chrysler", + "ford", + "gm", + "honda", + "hyundai", + "mazda", + "nissan", + "rivian", + "subaru" + "tesla", + "toyota", + "volkswagen", +}; + +BrandSettingsInterface* BrandSettingsFactory::createBrandSettings(const QString& brand, QWidget* parent) { + if (brand == "chrysler") + return new ChryslerSettings(parent); + if (brand == "ford") + return new FordSettings(parent); + if (brand == "gm") + return new GMSettings(parent); + if (brand == "honda") + return new HondaSettings(parent); + if (brand == "hyundai") + return new HyundaiSettings(parent); + if (brand == "mazda") + return new MazdaSettings(parent); + if (brand == "nissan") + return new NissanSettings(parent); + if (brand == "rivian") + return new RivianSettings(parent); + if (brand == "subaru") + return new SubaruSettings(parent); + if (brand == "tesla") + return new TeslaSettings(parent); + if (brand == "toyota") + return new ToyotaSettings(parent); + if (brand == "volkswagen") + return new VolkswagenSettings(parent); + + // Default empty settings if brand not supported + return nullptr; +} + +bool BrandSettingsFactory::isBrandSupported(const QString& brand) { + return supportedBrands.contains(brand); +} + +QStringList BrandSettingsFactory::getSupportedBrands() { + return supportedBrands; +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h new file mode 100644 index 0000000000..b4dc9fc81b --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +class BrandSettingsFactory { + +public: + static BrandSettingsInterface* createBrandSettings(const QString &brand, QWidget *parent = nullptr); + static bool isBrandSupported(const QString& brand); + static QStringList getSupportedBrands(); +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc new file mode 100644 index 0000000000..4c49207270 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h new file mode 100644 index 0000000000..2b6c57ae91 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include + +class BrandSettingsInterface : public QWidget { + Q_OBJECT + +public: + explicit BrandSettingsInterface(QWidget *parent = nullptr) : QWidget(parent) {} + virtual ~BrandSettingsInterface() = default; + + virtual void updatePanel(bool offroad) = 0; + virtual void updateSettings() = 0; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h new file mode 100644 index 0000000000..795dba5739 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h" diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc new file mode 100644 index 0000000000..0912a7afd8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +ChryslerSettings::ChryslerSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void ChryslerSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void ChryslerSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h new file mode 100644 index 0000000000..20a6235542 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class ChryslerSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit ChryslerSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc new file mode 100644 index 0000000000..eaae5a621a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +FordSettings::FordSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void FordSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void FordSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h new file mode 100644 index 0000000000..1d97651bab --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class FordSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit FordSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc new file mode 100644 index 0000000000..1aa4ccf938 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +GMSettings::GMSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void GMSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void GMSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h new file mode 100644 index 0000000000..35988d95e1 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class GMSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit GMSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc new file mode 100644 index 0000000000..1efdb747f3 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +HondaSettings::HondaSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void HondaSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void HondaSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h new file mode 100644 index 0000000000..e494e8001d --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class HondaSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit HondaSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc new file mode 100644 index 0000000000..7e4ea4326c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void HyundaiSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void HyundaiSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h new file mode 100644 index 0000000000..0f065124fe --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class HyundaiSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit HyundaiSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc new file mode 100644 index 0000000000..330afdc48c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +MazdaSettings::MazdaSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void MazdaSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void MazdaSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h new file mode 100644 index 0000000000..3d109fbe9e --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class MazdaSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit MazdaSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc new file mode 100644 index 0000000000..0faa6836fa --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +NissanSettings::NissanSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void NissanSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void NissanSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h new file mode 100644 index 0000000000..397a17e97b --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class NissanSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit NissanSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc new file mode 100644 index 0000000000..401048ee51 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +RivianSettings::RivianSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void RivianSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void RivianSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h new file mode 100644 index 0000000000..bf83f5966f --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class RivianSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit RivianSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc new file mode 100644 index 0000000000..7a021ef4c6 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +SubaruSettings::SubaruSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void SubaruSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void SubaruSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h new file mode 100644 index 0000000000..bdf7835595 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class SubaruSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit SubaruSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc new file mode 100644 index 0000000000..d811f19913 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +TeslaSettings::TeslaSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void TeslaSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void TeslaSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h new file mode 100644 index 0000000000..5c40324dc2 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class TeslaSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit TeslaSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc new file mode 100644 index 0000000000..13b4435d17 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +ToyotaSettings::ToyotaSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void ToyotaSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void ToyotaSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h new file mode 100644 index 0000000000..46c8ef9827 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class ToyotaSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit ToyotaSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc new file mode 100644 index 0000000000..457776f50a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h" + +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +VolkswagenSettings::VolkswagenSettings(QWidget *parent) : BrandSettingsInterface(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + ListWidget *list = new ListWidget(this, false); + + main_layout->addWidget(new ScrollViewSP(list, this)); +} + +void VolkswagenSettings::updatePanel(bool _offroad) { + updateSettings(); + + offroad = _offroad; +} + +void VolkswagenSettings::updateSettings() { + if (!isVisible()) { + return; + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h new file mode 100644 index 0000000000..c74123e7b2 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class VolkswagenSettings : public BrandSettingsInterface { + Q_OBJECT + +public: + explicit VolkswagenSettings(QWidget *parent = nullptr); + void updatePanel(bool _offroad); + void updateSettings(); + +private: + bool offroad = false; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc index cc3e6df183..7ba5790acb 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc @@ -7,6 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h" #include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { @@ -18,11 +20,14 @@ VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { vlayout->setContentsMargins(50, 20, 50, 20); platformSelector = new PlatformSelector(); + QObject::connect(platformSelector, &PlatformSelector::refreshPanel, this, &VehiclePanel::updateBrandSettings); list->addItem(platformSelector); ScrollViewSP *scroller = new ScrollViewSP(list, this); vlayout->addWidget(scroller); + currentBrandSettings = nullptr; + QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel); main_layout->addWidget(vehicleScreen); @@ -35,6 +40,26 @@ void VehiclePanel::showEvent(QShowEvent *event) { void VehiclePanel::updatePanel(bool _offroad) { platformSelector->refresh(_offroad); - + updateBrandSettings(); offroad = _offroad; } + +void VehiclePanel::updateBrandSettings() { + if (!isVisible()) { + return; + } + + if (currentBrandSettings) { + vehicleScreen->layout()->removeWidget(currentBrandSettings); + delete currentBrandSettings; + currentBrandSettings = nullptr; + } + + if (BrandSettingsFactory::isBrandSupported(platformSelector->brand)) { + currentBrandSettings = BrandSettingsFactory::createBrandSettings(platformSelector->brand, this); + if (currentBrandSettings) { + vehicleScreen->layout()->addWidget(currentBrandSettings); + currentBrandSettings->updatePanel(offroad); + } + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h index f6265da7a6..5fc03d3d68 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h @@ -9,6 +9,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h" class VehiclePanel : public QFrame { @@ -24,6 +25,10 @@ public slots: private: QStackedLayout* main_layout = nullptr; QWidget* vehicleScreen = nullptr; - PlatformSelector *platformSelector = nullptr; - bool offroad; + PlatformSelector* platformSelector = nullptr; + BrandSettingsInterface* currentBrandSettings = nullptr; + bool offroad = false; + +private slots: + void updateBrandSettings(); }; From 6142a52de7a5749ca3797c4bda794ef7223e065f Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sat, 10 May 2025 09:46:54 +0200 Subject: [PATCH 164/477] models: aligning naming on modeld_v2 with upstream naming (#903) Refactor variable names for clarity in 20Hz model logic. Renamed `full_features_20Hz` to `full_features_buffer` and `desire_20Hz` to `full_desire` for better readability and consistency. This improves code maintainability and aligns variable names with their intended purpose. --- sunnypilot/modeld_v2/modeld.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index e0f38c59ac..a52d844aeb 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -53,8 +53,8 @@ class ModelState: self.frames = {'input_imgs': DrivingModelFrame(context, buffer_length), 'big_input_imgs': DrivingModelFrame(context, buffer_length)} self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) if self.model_runner.is_20hz: - self.full_features_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32) - self.desire_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32) + self.full_features_buffer = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32) + self.full_desire = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32) # img buffers are managed in openCL transform code self.numpy_inputs = {} @@ -66,7 +66,7 @@ class ModelState: if self.model_runner.is_20hz: num_elements = self.numpy_inputs['features_buffer'].shape[1] step_size = int(-100 / num_elements) - self.full_features_20Hz_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] + self.full_features_buffer_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, self.numpy_inputs['desire'].shape[2]) def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, @@ -77,9 +77,9 @@ class ModelState: self.prev_desire[:] = inputs['desire'] if self.model_runner.is_20hz: - self.desire_20Hz[:-1] = self.desire_20Hz[1:] - self.desire_20Hz[-1] = new_desire - self.numpy_inputs['desire'][:] = self.desire_20Hz.reshape(self.desire_reshape_dims).max(axis=2) + self.full_desire[:-1] = self.full_desire[1:] + self.full_desire[-1] = new_desire + self.numpy_inputs['desire'][:] = self.full_desire.reshape(self.desire_reshape_dims).max(axis=2) else: length = inputs['desire'].shape[0] self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:] @@ -102,9 +102,9 @@ class ModelState: outputs = self.model_runner.run_model() if self.model_runner.is_20hz: - self.full_features_20Hz[:-1] = self.full_features_20Hz[1:] - self.full_features_20Hz[-1] = outputs['hidden_state'][0, :] - self.numpy_inputs['features_buffer'][:] = self.full_features_20Hz[self.full_features_20Hz_idxs] + self.full_features_buffer[:-1] = self.full_features_buffer[1:] + self.full_features_buffer[-1] = outputs['hidden_state'][0, :] + self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.full_features_buffer_idxs] else: feature_len = outputs['hidden_state'].shape[1] self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:] From 08efc252ecb9685c3f772b582a0b4c41c7f1d2bb Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 10 May 2025 12:34:58 -0400 Subject: [PATCH 165/477] ui: fix supported brand settings list (#905) --- .../qt/offroad/settings/vehicle/brand_settings_factory.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc index 08b137d270..bebc6c3032 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc @@ -17,7 +17,7 @@ static const QStringList supportedBrands = { "mazda", "nissan", "rivian", - "subaru" + "subaru", "tesla", "toyota", "volkswagen", From 8d06444bdda6b89b36e07fceb1c4514ca50089b6 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 10 May 2025 12:46:02 -0400 Subject: [PATCH 166/477] ui: consolidate panel updates at a single location (#904) * init * minimize changes for all brands * more * even less diff * more less * wow srsly --- .../vehicle/brand_settings_interface.cc | 13 ++++++++++ .../vehicle/brand_settings_interface.h | 13 +++++++--- .../settings/vehicle/chrysler_settings.cc | 17 ------------ .../settings/vehicle/chrysler_settings.h | 3 +-- .../offroad/settings/vehicle/ford_settings.cc | 17 ------------ .../offroad/settings/vehicle/ford_settings.h | 3 +-- .../offroad/settings/vehicle/gm_settings.cc | 17 ------------ .../qt/offroad/settings/vehicle/gm_settings.h | 3 +-- .../settings/vehicle/honda_settings.cc | 17 ------------ .../offroad/settings/vehicle/honda_settings.h | 3 +-- .../settings/vehicle/hyundai_settings.cc | 17 ------------ .../settings/vehicle/hyundai_settings.h | 6 +---- .../settings/vehicle/mazda_settings.cc | 17 ------------ .../offroad/settings/vehicle/mazda_settings.h | 3 +-- .../settings/vehicle/nissan_settings.cc | 17 ------------ .../settings/vehicle/nissan_settings.h | 3 +-- .../settings/vehicle/rivian_settings.cc | 17 ------------ .../settings/vehicle/rivian_settings.h | 3 +-- .../settings/vehicle/subaru_settings.cc | 17 ------------ .../settings/vehicle/subaru_settings.h | 3 +-- .../settings/vehicle/tesla_settings.cc | 17 ------------ .../offroad/settings/vehicle/tesla_settings.h | 3 +-- .../settings/vehicle/toyota_settings.cc | 17 ------------ .../settings/vehicle/toyota_settings.h | 3 +-- .../settings/vehicle/volkswagen_settings.cc | 17 ------------ .../settings/vehicle/volkswagen_settings.h | 3 +-- .../qt/offroad/settings/vehicle_panel.cc | 26 ++++++++++--------- .../qt/offroad/settings/vehicle_panel.h | 4 +-- 28 files changed, 51 insertions(+), 248 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc index 4c49207270..03921c01a3 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc @@ -6,3 +6,16 @@ */ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h" + +BrandSettingsInterface::BrandSettingsInterface(QWidget *parent) : QWidget(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(0, 0, 0, 0); + + list = new ListWidget(this, false); + main_layout->addWidget(list); +} + +void BrandSettingsInterface::updatePanel(bool _offroad) { + offroad = _offroad; + updateSettings(); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h index 2b6c57ae91..4aff123177 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h @@ -7,15 +7,22 @@ #pragma once -#include +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" class BrandSettingsInterface : public QWidget { Q_OBJECT public: - explicit BrandSettingsInterface(QWidget *parent = nullptr) : QWidget(parent) {} + explicit BrandSettingsInterface(QWidget *parent = nullptr); virtual ~BrandSettingsInterface() = default; - virtual void updatePanel(bool offroad) = 0; + void updatePanel(bool _offroad); virtual void updateSettings() = 0; + +protected: + ListWidget *list = nullptr; + Params params; + bool offroad = false; }; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc index 0912a7afd8..fd8fc423fe 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - ChryslerSettings::ChryslerSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void ChryslerSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void ChryslerSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h index 20a6235542..ab84bec16a 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h @@ -19,8 +19,7 @@ class ChryslerSettings : public BrandSettingsInterface { public: explicit ChryslerSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc index eaae5a621a..96564cb8a9 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - FordSettings::FordSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void FordSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void FordSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h index 1d97651bab..468bd6b7dd 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h @@ -19,8 +19,7 @@ class FordSettings : public BrandSettingsInterface { public: explicit FordSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc index 1aa4ccf938..060a28424b 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - GMSettings::GMSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void GMSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void GMSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h index 35988d95e1..211563ab61 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h @@ -19,8 +19,7 @@ class GMSettings : public BrandSettingsInterface { public: explicit GMSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc index 1efdb747f3..ca18fa7240 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - HondaSettings::HondaSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void HondaSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void HondaSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h index e494e8001d..e41602dc11 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h @@ -19,8 +19,7 @@ class HondaSettings : public BrandSettingsInterface { public: explicit HondaSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc index 7e4ea4326c..693a185cde 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void HyundaiSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void HyundaiSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h index 0f065124fe..edf495844d 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h @@ -19,9 +19,5 @@ class HyundaiSettings : public BrandSettingsInterface { public: explicit HyundaiSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); - -private: - bool offroad = false; + void updateSettings() override; }; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc index 330afdc48c..b8b8825f27 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - MazdaSettings::MazdaSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void MazdaSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void MazdaSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h index 3d109fbe9e..cbbc38de7f 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h @@ -19,8 +19,7 @@ class MazdaSettings : public BrandSettingsInterface { public: explicit MazdaSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc index 0faa6836fa..3c21943622 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - NissanSettings::NissanSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void NissanSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void NissanSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h index 397a17e97b..d6eabac0bf 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h @@ -19,8 +19,7 @@ class NissanSettings : public BrandSettingsInterface { public: explicit NissanSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc index 401048ee51..92cf3dcd6c 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - RivianSettings::RivianSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void RivianSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void RivianSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h index bf83f5966f..be25d01b37 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h @@ -19,8 +19,7 @@ class RivianSettings : public BrandSettingsInterface { public: explicit RivianSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc index 7a021ef4c6..47c4057f44 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - SubaruSettings::SubaruSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void SubaruSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void SubaruSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h index bdf7835595..a715951ad9 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h @@ -19,8 +19,7 @@ class SubaruSettings : public BrandSettingsInterface { public: explicit SubaruSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc index d811f19913..50ab023021 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - TeslaSettings::TeslaSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void TeslaSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void TeslaSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h index 5c40324dc2..37f2936cdf 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h @@ -19,8 +19,7 @@ class TeslaSettings : public BrandSettingsInterface { public: explicit TeslaSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc index 13b4435d17..c416177916 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - ToyotaSettings::ToyotaSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void ToyotaSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void ToyotaSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h index 46c8ef9827..9fc18a2a65 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h @@ -19,8 +19,7 @@ class ToyotaSettings : public BrandSettingsInterface { public: explicit ToyotaSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc index 457776f50a..59f0aab727 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc @@ -7,25 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h" -#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" - VolkswagenSettings::VolkswagenSettings(QWidget *parent) : BrandSettingsInterface(parent) { - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(0, 0, 0, 0); - - ListWidget *list = new ListWidget(this, false); - - main_layout->addWidget(new ScrollViewSP(list, this)); -} - -void VolkswagenSettings::updatePanel(bool _offroad) { - updateSettings(); - - offroad = _offroad; } void VolkswagenSettings::updateSettings() { - if (!isVisible()) { - return; - } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h index c74123e7b2..25d3a07faf 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h @@ -19,8 +19,7 @@ class VolkswagenSettings : public BrandSettingsInterface { public: explicit VolkswagenSettings(QWidget *parent = nullptr); - void updatePanel(bool _offroad); - void updateSettings(); + void updateSettings() override; private: bool offroad = false; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc index 7ba5790acb..5c00e746f8 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc @@ -12,26 +12,27 @@ #include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { - main_layout = new QStackedLayout(this); - ListWidget *list = new ListWidget(this); + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 20, 50, 20); - vehicleScreen = new QWidget(this); - QVBoxLayout *vlayout = new QVBoxLayout(vehicleScreen); - vlayout->setContentsMargins(50, 20, 50, 20); + ListWidget *list = new ListWidget(this); platformSelector = new PlatformSelector(); QObject::connect(platformSelector, &PlatformSelector::refreshPanel, this, &VehiclePanel::updateBrandSettings); list->addItem(platformSelector); + brandSettingsContainer = new QWidget(this); + brandSettingsContainerLayout = new QVBoxLayout(brandSettingsContainer); + brandSettingsContainerLayout->setContentsMargins(0, 0, 0, 0); + brandSettingsContainerLayout->setSpacing(0); + list->addItem(brandSettingsContainer); + ScrollViewSP *scroller = new ScrollViewSP(list, this); - vlayout->addWidget(scroller); + main_layout->addWidget(scroller); currentBrandSettings = nullptr; QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel); - - main_layout->addWidget(vehicleScreen); - main_layout->setCurrentWidget(vehicleScreen); } void VehiclePanel::showEvent(QShowEvent *event) { @@ -39,9 +40,9 @@ void VehiclePanel::showEvent(QShowEvent *event) { } void VehiclePanel::updatePanel(bool _offroad) { + offroad = _offroad; platformSelector->refresh(_offroad); updateBrandSettings(); - offroad = _offroad; } void VehiclePanel::updateBrandSettings() { @@ -50,7 +51,7 @@ void VehiclePanel::updateBrandSettings() { } if (currentBrandSettings) { - vehicleScreen->layout()->removeWidget(currentBrandSettings); + brandSettingsContainerLayout->removeWidget(currentBrandSettings); delete currentBrandSettings; currentBrandSettings = nullptr; } @@ -58,7 +59,8 @@ void VehiclePanel::updateBrandSettings() { if (BrandSettingsFactory::isBrandSupported(platformSelector->brand)) { currentBrandSettings = BrandSettingsFactory::createBrandSettings(platformSelector->brand, this); if (currentBrandSettings) { - vehicleScreen->layout()->addWidget(currentBrandSettings); + currentBrandSettings->setContentsMargins(0, 0, 0, 0); + brandSettingsContainerLayout->addWidget(currentBrandSettings); currentBrandSettings->updatePanel(offroad); } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h index 5fc03d3d68..a170aa87c3 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h @@ -23,10 +23,10 @@ public slots: void updatePanel(bool _offroad); private: - QStackedLayout* main_layout = nullptr; - QWidget* vehicleScreen = nullptr; PlatformSelector* platformSelector = nullptr; BrandSettingsInterface* currentBrandSettings = nullptr; + QWidget* brandSettingsContainer = nullptr; + QVBoxLayout* brandSettingsContainerLayout = nullptr; bool offroad = false; private slots: From bf6123c4ada8534bf4104d1a2f4abf84d65cb6fc Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 10 May 2025 12:58:08 -0400 Subject: [PATCH 167/477] Reapply "Hyundai: custom longitudinal tuning" (#894) * Reapply "Hyundai: custom longitudinal tuning" (#892) This reverts commit 68c593db5f1c9677eee71dd2f7b66dc7f6e02ba0. * fix panel behavior * dynamic description * try to merge and use the same scrollview * fix for all to update * dynamic update and fix description * minimize changes for all brands * init * minimize changes for all brands * more * even less diff * more * even less diff * more less * wow srsly * more less * wow srsly --- common/params_keys.h | 1 + .../settings/vehicle/hyundai_settings.cc | 43 +++++++++++++++++++ .../settings/vehicle/hyundai_settings.h | 42 ++++++++++++++++++ sunnypilot/selfdrive/car/interfaces.py | 19 +++++++- system/manager/manager.py | 1 + 5 files changed, 105 insertions(+), 1 deletion(-) diff --git a/common/params_keys.h b/common/params_keys.h index 575dcc2adc..97f23d5108 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -164,6 +164,7 @@ inline static std::unordered_map keys = { {"BackupManager_RestoreVersion", PERSISTENT}, // sunnypilot car specific params + {"HyundaiLongitudinalTuning", PERSISTENT}, {"HyundaiRadarTracks", PERSISTENT}, {"HyundaiRadarTracksConfirmed", PERSISTENT}, {"HyundaiRadarTracksPersistent", PERSISTENT}, diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc index 693a185cde..5f0010ac33 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc @@ -8,7 +8,50 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) { + std::vector tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") }; + longitudinalTuningToggle = new ButtonParamControl( + "HyundaiLongitudinalTuning", + tr("Custom Longitudinal Tuning"), + "", + "", + tuning_texts, + 500 + ); + QObject::connect(longitudinalTuningToggle, &ButtonParamControlSP::buttonToggled, this, &HyundaiSettings::updateSettings); + list->addItem(longitudinalTuningToggle); + longitudinalTuningToggle->showDescription(); } void HyundaiSettings::updateSettings() { + auto longitudinal_tuning_param = std::atoi(params.get("HyundaiLongitudinalTuning").c_str()); + + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + has_longitudinal_control = hasLongitudinalControl(CP); + } else { + has_longitudinal_control = false; + } + + LongitudinalTuningOption longitudinal_tuning_option; + if (longitudinal_tuning_param == int(LongitudinalTuningOption::PREDICTIVE)) { + longitudinal_tuning_option = LongitudinalTuningOption::PREDICTIVE; + } else if (longitudinal_tuning_param == int(LongitudinalTuningOption::DYNAMIC)) { + longitudinal_tuning_option = LongitudinalTuningOption::DYNAMIC; + } else { + longitudinal_tuning_option = LongitudinalTuningOption::OFF; + } + + bool longitudinal_tuning_disabled = !offroad || !has_longitudinal_control; + QString longitudinal_tuning_description = longitudinalTuningDescription(longitudinal_tuning_option); + if (longitudinal_tuning_disabled) { + longitudinal_tuning_description = toggleDisableMsg(offroad, has_longitudinal_control); + } + + longitudinalTuningToggle->setEnabled(!longitudinal_tuning_disabled); + longitudinalTuningToggle->setDescription(longitudinal_tuning_description); + longitudinalTuningToggle->showDescription(); } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h index edf495844d..c94d40cfde 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h @@ -14,10 +14,52 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +enum class LongitudinalTuningOption { + OFF, + DYNAMIC, + PREDICTIVE, +}; + class HyundaiSettings : public BrandSettingsInterface { Q_OBJECT public: explicit HyundaiSettings(QWidget *parent = nullptr); void updateSettings() override; + +private: + bool has_longitudinal_control = false; + ButtonParamControl *longitudinalTuningToggle = nullptr; + + static QString toggleDisableMsg(bool _offroad, bool _has_longitudinal_control) { + if (!_has_longitudinal_control) { + return tr("This feature can only be used with openpilot longitudinal control enabled."); + } + + if (!_offroad) { + return tr("Enable \"Always Offroad\" in Device panel, or turn vehicle off to select an option."); + } + + return QString(); + } + + static QString longitudinalTuningDescription(LongitudinalTuningOption option = LongitudinalTuningOption::OFF) { + QString off_str = tr("Off: Uses default tuning"); + QString dynamic_str = tr("Dynamic: Adjusts acceleration limits based on current speed"); + QString predictive_str = tr("Predictive: Uses future trajectory data to anticipate needed adjustments"); + + if (option == LongitudinalTuningOption::PREDICTIVE) { + predictive_str = "" + predictive_str + ""; + } else if (option == LongitudinalTuningOption::DYNAMIC) { + dynamic_str = "" + dynamic_str + ""; + } else { + off_str = "" + off_str + ""; + } + + return QString("%1

%2
%3
%4
") + .arg(tr("Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.")) + .arg(off_str) + .arg(dynamic_str) + .arg(predictive_str); + } }; diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index 2c27e960e9..f2019f5ed7 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -11,6 +11,7 @@ from opendbc.car.car_helpers import can_fingerprint from opendbc.car.interfaces import CarInterfaceBase from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC +from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog @@ -25,6 +26,21 @@ def log_fingerprint(CP: structs.CarParams) -> None: else: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) +def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, + params: Params = None) -> None: + if params is None: + params = Params() + + # Hyundai Custom Longitudinal Tuning + if CP.brand == 'hyundai': + hyundai_longitudinal_tuning = int(params.get("HyundaiLongitudinalTuning", encoding="utf8") or 0) + if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC: + CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value + if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE: + CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value + + CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP) + def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None, enabled: bool = False) -> None: @@ -61,10 +77,11 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, CP.radarUnavailable = False -def setup_interfaces(CI: CarInterfaceBase, params: Params = None): +def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None: CP = CI.CP CP_SP = CI.CP_SP + _initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params) _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) _initialize_radar_tracks(CP, CP_SP, params) diff --git a/system/manager/manager.py b/system/manager/manager.py index fde85f0f7c..90df415a8c 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -45,6 +45,7 @@ def manager_init() -> None: ("AutoLaneChangeTimer", "0"), ("AutoLaneChangeBsmDelay", "0"), ("DynamicExperimentalControl", "0"), + ("HyundaiLongitudinalTuning", "0"), ("Mads", "1"), ("MadsMainCruiseAllowed", "1"), ("MadsPauseLateralOnBrake", "0"), From 3b94e6f92f922135d05d3d72c150f5d3b65819b0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 11 May 2025 00:59:42 +0800 Subject: [PATCH 168/477] system/ui: add tethering support to WifiManager (#35167) add tethering support to WifiManager --- system/ui/lib/wifi_manager.py | 160 ++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 990e1d14a1..ff18092364 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -12,6 +12,7 @@ from dbus_next.aio import MessageBus from dbus_next import BusType, Variant, Message from dbus_next.errors import DBusError from dbus_next.constants import MessageType +from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog T = TypeVar("T") @@ -29,6 +30,9 @@ NM_DEVICE_IFACE = "org.freedesktop.NetworkManager.Device" NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8 +TETHERING_IP_ADDRESS = "192.168.43.1" +DEFAULT_TETHERING_PASSWORD = "12345678" + # NetworkManager device states class NMDeviceState(IntEnum): DISCONNECTED = 30 @@ -72,6 +76,7 @@ class WifiManager: self.saved_connections: dict[str, str] = {} self.active_ap_path: str = "" self.scan_task: asyncio.Task | None = None + self._tethering_ssid = "weedle-" + Params().get("DongleId", encoding="utf-8") self.running: bool = True async def connect(self) -> None: @@ -83,6 +88,7 @@ class WifiManager: await self._setup_signals(self.device_path) self.active_ap_path = await self.get_active_access_point() + await self.add_tethering_connection(self._tethering_ssid, DEFAULT_TETHERING_PASSWORD) self.saved_connections = await self._get_saved_connections() self.scan_task = asyncio.create_task(self._periodic_scan()) except DBusError as e: @@ -199,6 +205,160 @@ class WifiManager: return False + async def add_tethering_connection(self, ssid: str, password: str = "12345678") -> bool: + """Create a WiFi tethering connection.""" + if len(password) < 8: + print("Tethering password must be at least 8 characters") + return False + + try: + # First, check if a hotspot connection already exists + settings_iface = await self._get_interface(NM, NM_SETTINGS_PATH, NM_SETTINGS_IFACE) + connection_paths = await settings_iface.call_list_connections() + + # Look for an existing hotspot connection + for path in connection_paths: + try: + settings = await self._get_connection_settings(path) + conn_type = settings.get('connection', {}).get('type', Variant('s', '')).value + wifi_mode = settings.get('802-11-wireless', {}).get('mode', Variant('s', '')).value + + if conn_type == '802-11-wireless' and wifi_mode == 'ap': + # Extract the SSID to check + connection_ssid = self._extract_ssid(settings) + if connection_ssid == ssid: + return True + except DBusError: + continue + + connection = { + 'connection': { + 'id': Variant('s', 'Hotspot'), + 'uuid': Variant('s', str(uuid.uuid4())), + 'type': Variant('s', '802-11-wireless'), + 'interface-name': Variant('s', 'wlan0'), + 'autoconnect': Variant('b', False), + }, + '802-11-wireless': { + 'band': Variant('s', 'bg'), + 'mode': Variant('s', 'ap'), + 'ssid': Variant('ay', ssid.encode('utf-8')), + }, + '802-11-wireless-security': { + 'group': Variant('as', ['ccmp']), + 'key-mgmt': Variant('s', 'wpa-psk'), + 'pairwise': Variant('as', ['ccmp']), + 'proto': Variant('as', ['rsn']), + 'psk': Variant('s', password), + }, + 'ipv4': { + 'method': Variant('s', 'shared'), + 'address-data': Variant('aa{sv}', [{'address': Variant('s', TETHERING_IP_ADDRESS), 'prefix': Variant('u', 24)}]), + 'gateway': Variant('s', TETHERING_IP_ADDRESS), + 'never-default': Variant('b', True), + }, + 'ipv6': { + 'method': Variant('s', 'ignore'), + }, + } + + settings_iface = await self._get_interface(NM, NM_SETTINGS_PATH, NM_SETTINGS_IFACE) + new_connection = await settings_iface.call_add_connection(connection) + print(f"Added tethering connection with path: {new_connection}") + return True + except DBusError as e: + print(f"Failed to add tethering connection: {e}") + return False + except Exception as e: + print(f"Unexpected error adding tethering connection: {e}") + return False + + async def get_tethering_password(self) -> str: + """Get the current tethering password.""" + try: + hotspot_path = self.saved_connections.get(self._tethering_ssid) + if hotspot_path: + conn_iface = await self._get_interface(NM, hotspot_path, NM_CONNECTION_IFACE) + secrets = await conn_iface.call_get_secrets('802-11-wireless-security') + if secrets and '802-11-wireless-security' in secrets: + psk = secrets.get('802-11-wireless-security', {}).get('psk', Variant('s', '')).value + return str(psk) if psk is not None else "" + return "" + except DBusError as e: + print(f"Failed to get tethering password: {e}") + return "" + except Exception as e: + print(f"Unexpected error getting tethering password: {e}") + return "" + + async def set_tethering_password(self, password: str) -> bool: + """Set the tethering password.""" + if len(password) < 8: + cloudlog.error("Tethering password must be at least 8 characters") + return False + + try: + hotspot_path = self.saved_connections.get(self._tethering_ssid) + if not hotspot_path: + print("No hotspot connection found") + return False + + # Update the connection settings with new password + settings = await self._get_connection_settings(hotspot_path) + if '802-11-wireless-security' not in settings: + settings['802-11-wireless-security'] = {} + settings['802-11-wireless-security']['psk'] = Variant('s', password) + + # Apply changes + conn_iface = await self._get_interface(NM, hotspot_path, NM_CONNECTION_IFACE) + await conn_iface.call_update(settings) + + # Check if connection is active and restart if needed + is_active = False + nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) + active_connections = await nm_iface.get_active_connections() + + for conn_path in active_connections: + props_iface = await self._get_interface(NM, conn_path, NM_PROPERTIES_IFACE) + conn_id_path = await props_iface.call_get('org.freedesktop.NetworkManager.Connection.Active', 'Connection') + if conn_id_path.value == hotspot_path: + is_active = True + await nm_iface.call_deactivate_connection(conn_path) + break + + if is_active: + await nm_iface.call_activate_connection(hotspot_path, self.device_path, "/") + + print("Tethering password updated successfully") + return True + except DBusError as e: + print(f"Failed to set tethering password: {e}") + return False + except Exception as e: + print(f"Unexpected error setting tethering password: {e}") + return False + + async def is_tethering_active(self) -> bool: + """Check if tethering is active for the specified SSID.""" + try: + hotspot_path = self.saved_connections.get(self._tethering_ssid) + if not hotspot_path: + return False + + nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) + active_connections = await nm_iface.get_active_connections() + + for conn_path in active_connections: + props_iface = await self._get_interface(NM, conn_path, NM_PROPERTIES_IFACE) + conn_id_path = await props_iface.call_get('org.freedesktop.NetworkManager.Connection.Active', 'Connection') + + if conn_id_path.value == hotspot_path: + return True + + return False + except Exception: + return False + async def _periodic_scan(self): while self.running: try: From 7147c2695447492a6b456674a4dd1b30b344084e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 11 May 2025 01:00:28 +0800 Subject: [PATCH 169/477] system/ui: fix WIFI authentication callback and connection tracking (#35169) fix authentication callback and state handling --- system/ui/lib/wifi_manager.py | 22 +++++++++++++++++++--- system/ui/widgets/network.py | 11 ++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index ff18092364..d3d9bb4414 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -61,7 +61,7 @@ class NetworkInfo: @dataclass class WifiManagerCallbacks: - need_auth: Callable[[], None] | None = None + need_auth: Callable[[str], None] | None = None activated: Callable[[], None] | None = None forgotten: Callable[[], None] | None = None @@ -78,6 +78,7 @@ class WifiManager: self.scan_task: asyncio.Task | None = None self._tethering_ssid = "weedle-" + Params().get("DongleId", encoding="utf-8") self.running: bool = True + self._current_connection_ssid: str | None = None async def connect(self) -> None: """Connect to the DBus system bus.""" @@ -133,6 +134,9 @@ class WifiManager: try: nm_iface = await self._get_interface(NM, path, NM_CONNECTION_IFACE) await nm_iface.call_delete() + if self._current_connection_ssid == ssid: + self._current_connection_ssid = None + del self.saved_connections[ssid] return True except DBusError as e: cloudlog.error(f"Failed to delete connection for SSID: {ssid}. Error: {e}") @@ -153,6 +157,7 @@ class WifiManager: async def connect_to_network(self, ssid: str, password: str = None, bssid: str = None, is_hidden: bool = False) -> None: """Connect to a selected Wi-Fi network.""" try: + self._current_connection_ssid = ssid connection = { 'connection': { 'type': Variant('s', '802-11-wireless'), @@ -182,8 +187,8 @@ class WifiManager: nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) await nm_iface.call_add_and_activate_connection(connection, self.device_path, "/") await self._update_connection_status() - except DBusError as e: + self._current_connection_ssid = None cloudlog.error(f"Error connecting to network: {e}") def is_saved(self, ssid: str) -> bool: @@ -403,11 +408,22 @@ class WifiManager: if self.callbacks.activated: self.callbacks.activated() asyncio.create_task(self._update_connection_status()) + self._current_connection_ssid = None elif new_state in (NMDeviceState.DISCONNECTED, NMDeviceState.NEED_AUTH): for network in self.networks: network.is_connected = False if new_state == NMDeviceState.NEED_AUTH and reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and self.callbacks.need_auth: - self.callbacks.need_auth() + if self._current_connection_ssid: + self.callbacks.need_auth(self._current_connection_ssid) + else: + # Try to find the network from active_ap_path + for network in self.networks: + if network.path == self.active_ap_path: + self.callbacks.need_auth(network.ssid) + break + else: + # Couldn't identify the network that needs auth + cloudlog.error("Network needs authentication but couldn't identify which one") def _on_new_connection(self, path: str) -> None: """Callback for NewConnection signal.""" diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 59427b57b8..0e9ad349b6 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -142,10 +142,15 @@ class WifiManagerUI: self.state = StateForgetting(network) self.wifi_manager.forget_connection(network.ssid) - def _on_need_auth(self): + def _on_need_auth(self, ssid): match self.state: - case StateConnecting(network): - self.state = StateNeedsAuth(network) + case StateConnecting(ssid): + self.state = StateNeedsAuth(ssid) + case _: + # Find network by SSID + network = next((n for n in self.wifi_manager.networks if n.ssid == ssid), None) + if network: + self.state = StateNeedsAuth(network) def _on_activated(self): if isinstance(self.state, StateConnecting): From eaa595958c15f958cb248fb9287672e201cbd369 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 11 May 2025 05:22:06 +0800 Subject: [PATCH 170/477] system/ui: prevent duplicate WiFi connections when retrying with new password (#35174) Prevent duplicate WiFi connections when retrying with new password --- system/ui/lib/wifi_manager.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index d3d9bb4414..29d3231d5f 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -136,7 +136,10 @@ class WifiManager: await nm_iface.call_delete() if self._current_connection_ssid == ssid: self._current_connection_ssid = None - del self.saved_connections[ssid] + + if ssid in self.saved_connections: + del self.saved_connections[ssid] + return True except DBusError as e: cloudlog.error(f"Failed to delete connection for SSID: {ssid}. Error: {e}") @@ -158,6 +161,17 @@ class WifiManager: """Connect to a selected Wi-Fi network.""" try: self._current_connection_ssid = ssid + + if ssid in self.saved_connections: + # Forget old connection if new password provided + if password: + await self.forget_connection(ssid) + await asyncio.sleep(0.2) # NetworkManager delay + else: + # Just activate existing connection + await self.activate_connection(ssid) + return + connection = { 'connection': { 'type': Variant('s', '802-11-wireless'), From 6c1edca8061342754794dafd1522172700837419 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 10 May 2025 18:51:37 -0400 Subject: [PATCH 171/477] MADS: use low velocity threshold for silent wrong gear checks (#901) * MADS: use low velocity threshold for silent wrong gear checks * higher --- sunnypilot/mads/mads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 2d7d41b4bc..d27e2f1fcb 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -72,7 +72,7 @@ class ModularAssistiveDrivingSystem: if self.events.has(EventName.seatbeltNotLatched): replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched) transition_paused_state() - if self.events.has(EventName.wrongGear) and (CS.standstill or CS.gearShifter == GearShifter.reverse): + if self.events.has(EventName.wrongGear) and (CS.vEgo < 2.5 or CS.gearShifter == GearShifter.reverse): replace_event(EventName.wrongGear, EventNameSP.silentWrongGear) transition_paused_state() if self.events.has(EventName.reverseGear): From 921b51f56fdddfc4f075e3dff3f7f2a7fe3ed5a7 Mon Sep 17 00:00:00 2001 From: Nayan Date: Sat, 10 May 2025 19:01:37 -0400 Subject: [PATCH 172/477] ui: display actual fingerprint name with auto-fingerprint (#906) use platform directly Co-authored-by: Jason Wen --- .../qt/offroad/settings/vehicle/platform_selector.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc index 21c9a44cb4..f31e8e3290 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc @@ -70,14 +70,6 @@ void PlatformSelector::refresh(bool _offroad) { platform = QString::fromStdString(CP.getCarFingerprint().cStr()); - for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) { - if (it.value()["platform"].toString() == platform) { - platform = it.key(); - brand = it.value()["brand"].toString(); - break; - } - } - if (platform == "MOCK") { platform = unrecognized_str; } else { From 4ceb1ecd190e3ede31da4a636723b1bb72196313 Mon Sep 17 00:00:00 2001 From: Nayan Date: Sat, 10 May 2025 19:10:15 -0400 Subject: [PATCH 173/477] ui: fix description margins on `AbstractControlSP_SELECTOR` (#902) fix margins for description on AbstractControlSP_SELECTOR Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/qt/widgets/controls.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc index 551907c612..d873913b57 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.cc +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.cc @@ -148,7 +148,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con // description description = new QLabel(desc); - description->setContentsMargins(0, 20, 40, 20); + description->setContentsMargins(40, 20, 40, 20); description->setStyleSheet("font-size: 40px; color: grey"); description->setWordWrap(true); description->setVisible(false); From 3a4c74b67d0e293622d5cfcca2f54cce1dc73f96 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 10 May 2025 21:47:24 -0400 Subject: [PATCH 174/477] Revert "ui: display actual fingerprint name with auto-fingerprint" (#907) Revert "ui: display actual fingerprint name with auto-fingerprint (#906)" This reverts commit 921b51f56fdddfc4f075e3dff3f7f2a7fe3ed5a7. --- .../qt/offroad/settings/vehicle/platform_selector.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc index f31e8e3290..21c9a44cb4 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc @@ -70,6 +70,14 @@ void PlatformSelector::refresh(bool _offroad) { platform = QString::fromStdString(CP.getCarFingerprint().cStr()); + for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) { + if (it.value()["platform"].toString() == platform) { + platform = it.key(); + brand = it.value()["brand"].toString(); + break; + } + } + if (platform == "MOCK") { platform = unrecognized_str; } else { From 5c9ce2a042a20d4c058525bc73b00045fb947921 Mon Sep 17 00:00:00 2001 From: Nayan Date: Sat, 10 May 2025 23:35:02 -0400 Subject: [PATCH 175/477] ui: display actual fingerprint name with auto-fingerprint (#908) * use platform directly * Revert "use platform directly" This reverts commit b71c315d308a5f8f7cd2091de9ce405fcc51074d. * update brand from platform package --- .../sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc index 21c9a44cb4..0d4dde681f 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc @@ -72,7 +72,6 @@ void PlatformSelector::refresh(bool _offroad) { for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) { if (it.value()["platform"].toString() == platform) { - platform = it.key(); brand = it.value()["brand"].toString(); break; } From f1760e63d39d1be2ed470364e435b2cd981c8e45 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 11 May 2025 15:26:25 +0800 Subject: [PATCH 176/477] system/ui: performance optimizations for WiFi Manager with cached network data (#35170) Performance optimizations for WiFi Manager with cached network data --- system/ui/lib/wifi_manager.py | 15 ++++++++++----- system/ui/widgets/network.py | 22 ++++++++++++++-------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 29d3231d5f..00649b146b 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -1,5 +1,6 @@ import asyncio import concurrent.futures +import copy import threading import time import uuid @@ -56,6 +57,7 @@ class NetworkInfo: security_type: SecurityType path: str bssid: str + is_saved: bool = False # saved_path: str @@ -64,6 +66,7 @@ class WifiManagerCallbacks: need_auth: Callable[[str], None] | None = None activated: Callable[[], None] | None = None forgotten: Callable[[], None] | None = None + networks_updated: Callable[[list[NetworkInfo]], None] | None = None class WifiManager: @@ -452,6 +455,8 @@ class WifiManager: del self.saved_connections[ssid] if self.callbacks.forgotten: self.callbacks.forgotten() + # Update network list to reflect the removed saved connection + asyncio.create_task(self._update_connection_status()) break async def _add_saved_connection(self, path: str) -> None: @@ -460,6 +465,7 @@ class WifiManager: settings = await self._get_connection_settings(path) if ssid := self._extract_ssid(settings): self.saved_connections[ssid] = path + await self._update_connection_status() except DBusError as e: cloudlog.error(f"Failed to add connection {path}: {e}") @@ -517,6 +523,7 @@ class WifiManager: path=ap_path, bssid=bssid, is_connected=self.active_ap_path == ap_path, + is_saved=ssid in self.saved_connections ) except DBusError as e: @@ -533,6 +540,9 @@ class WifiManager: ), ) + if self.callbacks.networks_updated: + self.callbacks.networks_updated(copy.deepcopy(self.networks)) + async def _get_connection_settings(self, path): """Fetch connection settings for a specific connection path.""" try: @@ -628,11 +638,6 @@ class WifiManagerWrapper: self._thread.join(timeout=2.0) self._running = False - @property - def networks(self) -> list[NetworkInfo]: - """Get the current list of networks.""" - return self._run_coroutine_sync(lambda manager: manager.networks.copy(), default=[]) - def is_saved(self, ssid: str) -> bool: """Check if a network is saved.""" return self._run_coroutine_sync(lambda manager: manager.is_saved(ssid), default=False) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 0e9ad349b6..14f4017925 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -48,13 +48,15 @@ class WifiManagerUI: self.scroll_panel = GuiScrollPanel() self.keyboard = Keyboard() + self._networks: list[NetworkInfo] = [] + self.wifi_manager = wifi_manager - self.wifi_manager.set_callbacks(WifiManagerCallbacks(self._on_need_auth, self._on_activated, self._on_forgotten)) + self.wifi_manager.set_callbacks(WifiManagerCallbacks(self._on_need_auth, self._on_activated, self._on_forgotten, self._on_network_updated)) self.wifi_manager.start() self.wifi_manager.connect() def render(self, rect: rl.Rectangle): - if not self.wifi_manager.networks: + if not self._networks: gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) return @@ -77,19 +79,19 @@ class WifiManagerUI: self._draw_network_list(rect) def _draw_network_list(self, rect: rl.Rectangle): - content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self.wifi_manager.networks) * ITEM_HEIGHT) + content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT) offset = self.scroll_panel.handle_scroll(rect, content_rect) clicked = self.scroll_panel.is_click_valid() rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - for i, network in enumerate(self.wifi_manager.networks): + for i, network in enumerate(self._networks): y_offset = rect.y + i * ITEM_HEIGHT + offset.y item_rect = rl.Rectangle(rect.x, y_offset, rect.width, ITEM_HEIGHT) if not rl.check_collision_recs(item_rect, rect): continue self._draw_network_item(item_rect, network, clicked) - if i < len(self.wifi_manager.networks) - 1: + if i < len(self._networks) - 1: line_y = int(item_rect.y + item_rect.height - 1) rl.draw_line(int(item_rect.x), int(line_y), int(item_rect.x + item_rect.width), line_y, rl.LIGHTGRAY) @@ -115,7 +117,7 @@ class WifiManagerUI: rl.gui_label(state_rect, status_text) # If the network is saved, show the "Forget" button - if self.wifi_manager.is_saved(network.ssid): + if network.is_saved: forget_btn_rect = rl.Rectangle( rect.x + rect.width - self.btn_width, rect.y + (ITEM_HEIGHT - 80) / 2, @@ -126,22 +128,26 @@ class WifiManagerUI: self.state = StateShowForgetConfirm(network) if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), label_rect) and clicked: - if not self.wifi_manager.is_saved(network.ssid): + if not network.is_saved: self.state = StateNeedsAuth(network) else: self.connect_to_network(network) def connect_to_network(self, network: NetworkInfo, password=''): self.state = StateConnecting(network) - if self.wifi_manager.is_saved(network.ssid) and not password: + if network.is_saved and not password: self.wifi_manager.activate_connection(network.ssid) else: self.wifi_manager.connect_to_network(network.ssid, password) def forget_network(self, network: NetworkInfo): self.state = StateForgetting(network) + network.is_saved = False self.wifi_manager.forget_connection(network.ssid) + def _on_network_updated(self, networks: list[NetworkInfo]): + self._networks = networks + def _on_need_auth(self, ssid): match self.state: case StateConnecting(ssid): From bbeb37d7266a98ba88abda27e869e8178a41aafb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 11 May 2025 15:43:23 +0800 Subject: [PATCH 177/477] system/ui: add MultiOptionDialog for selection from scrollable lists (#35176) * add MultiOptionDialog for selection from scrollable lists * mv --------- Co-authored-by: Cameron Clough --- system/ui/lib/button.py | 21 ++++++-- system/ui/widgets/option_dialog.py | 81 ++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 system/ui/widgets/option_dialog.py diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index 034189275f..1857fd21cc 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -10,6 +10,12 @@ class ButtonStyle(IntEnum): TRANSPARENT = 3 # For buttons with transparent background and border +class TextAlignment(IntEnum): + LEFT = 0 + CENTER = 1 + RIGHT = 2 + + DEFAULT_BUTTON_FONT_SIZE = 60 BUTTON_ENABLED_TEXT_COLOR = rl.Color(228, 228, 228, 255) BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) @@ -38,6 +44,8 @@ def gui_button( button_style: ButtonStyle = ButtonStyle.NORMAL, is_enabled: bool = True, border_radius: int = 10, # Corner rounding in pixels + text_alignment: TextAlignment = TextAlignment.CENTER, + text_padding: int = 20, # Padding for left/right alignment ) -> int: result = 0 @@ -58,11 +66,16 @@ def gui_button( rl.draw_rectangle_rounded_lines_ex(rect, roundness, 20, 2, rl.WHITE) font = gui_app.font(font_weight) - # Center text in the button text_size = rl.measure_text_ex(font, text, font_size, 0) - text_pos = rl.Vector2( - rect.x + (rect.width - text_size.x) // 2, rect.y + (rect.height - text_size.y) // 2 - ) + text_pos = rl.Vector2(0, rect.y + (rect.height - text_size.y) // 2) # Vertical centering + + # Horizontal alignment + if text_alignment == TextAlignment.LEFT: + text_pos.x = rect.x + text_padding + elif text_alignment == TextAlignment.CENTER: + text_pos.x = rect.x + (rect.width - text_size.x) // 2 + elif text_alignment == TextAlignment.RIGHT: + text_pos.x = rect.x + rect.width - text_size.x - text_padding # Draw the button text text_color = BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR diff --git a/system/ui/widgets/option_dialog.py b/system/ui/widgets/option_dialog.py new file mode 100644 index 0000000000..c25c5c6e6c --- /dev/null +++ b/system/ui/widgets/option_dialog.py @@ -0,0 +1,81 @@ +import pyray as rl + +from openpilot.system.ui.lib.button import gui_button, ButtonStyle, TextAlignment +from openpilot.system.ui.lib.label import gui_label +from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel + + +class MultiOptionDialog: + def __init__(self, title, options, current=""): + self._title = title + self._options = options + self._current = current if current in options else "" + self._selection = self._current + self._option_height = 80 + self._padding = 20 + self.scroll_panel = GuiScrollPanel() + + @property + def selection(self): + return self._selection + + def render(self, rect): + title_rect = rl.Rectangle(rect.x + self._padding, rect.y + self._padding, rect.width - 2 * self._padding, 70) + gui_label(title_rect, self._title, 70) + + options_y_start = rect.y + 120 + options_height = len(self._options) * (self._option_height + 10) + options_rect = rl.Rectangle(rect.x + self._padding, options_y_start, rect.width - 2 * self._padding, options_height) + + view_rect = rl.Rectangle( + rect.x + self._padding, options_y_start, rect.width - 2 * self._padding, rect.height - 200 - 2 * self._padding + ) + + offset = self.scroll_panel.handle_scroll(view_rect, options_rect) + is_click_valid = self.scroll_panel.is_click_valid() + + rl.begin_scissor_mode(int(view_rect.x), int(view_rect.y), int(view_rect.width), int(view_rect.height)) + + for i, option in enumerate(self._options): + y_pos = view_rect.y + i * (self._option_height + 10) + offset.y + item_rect = rl.Rectangle(view_rect.x, y_pos, view_rect.width, self._option_height) + + if not rl.check_collision_recs(item_rect, view_rect): + continue + + is_selected = option == self._selection + button_style = ButtonStyle.PRIMARY if is_selected else ButtonStyle.NORMAL + + if gui_button(item_rect, option, button_style=button_style, text_alignment=TextAlignment.LEFT) and is_click_valid: + self._selection = option + + rl.end_scissor_mode() + + button_y = rect.y + rect.height - 80 - self._padding + button_width = (rect.width - 3 * self._padding) / 2 + + cancel_rect = rl.Rectangle(rect.x + self._padding, button_y, button_width, 80) + if gui_button(cancel_rect, "Cancel"): + return 0 # Canceled + + select_rect = rl.Rectangle(rect.x + 2 * self._padding + button_width, button_y, button_width, 80) + has_new_selection = self._selection != "" and self._selection != self._current + + if gui_button(select_rect, "Select", is_enabled=has_new_selection, button_style=ButtonStyle.PRIMARY): + return 1 # Selected + + return -1 # Still active + + +if __name__ == "__main__": + from openpilot.system.ui.lib.application import gui_app + + gui_app.init_window("Multi Option Dialog Example") + options = [f"Option {i}" for i in range(1, 11)] + dialog = MultiOptionDialog("Choose an option", options, options[0]) + + for _ in gui_app.render(): + result = dialog.render(rl.Rectangle(100, 100, 1024, 800)) + if result >= 0: + print(f"Selected: {dialog.selection}" if result > 0 else "Canceled") + break From 01867f1b2b59e817f185b6f3f7427080b1a43de1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 11 May 2025 21:40:06 +0800 Subject: [PATCH 178/477] system/ui: integrate WifiManagerUI into Updater (#35177) integrate WifiManagerUI into Updater --- system/ui/updater.py | 51 +++++++++++--------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/system/ui/updater.py b/system/ui/updater.py index eb9d766a16..42acaf5675 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -9,6 +9,9 @@ from openpilot.system.hardware import HARDWARE from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_text_box, gui_label +from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper +from openpilot.system.ui.widgets.network import WifiManagerUI + # Constants MARGIN = 50 @@ -39,6 +42,8 @@ class Updater: self.show_reboot_button = False self.process = None self.update_thread = None + self.wifi_manager = WifiManagerWrapper() + self.wifi_manager_ui = WifiManagerUI(self.wifi_manager) def install_update(self): self.current_screen = Screen.PROGRESS @@ -79,8 +84,9 @@ class Updater: gui_label(title_rect, "Update Required", TITLE_FONT_SIZE, font_weight=FontWeight.BOLD) # Description - desc_text = "An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. \ - The download size is approximately 1GB." + desc_text = ("An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. " + + "The download size is approximately 1GB.") + desc_rect = rl.Rectangle(MARGIN + 50, 250 + TITLE_FONT_SIZE + 75, gui_app.width - MARGIN * 2 - 100, BODY_FONT_SIZE * 3) gui_text_box(desc_rect, desc_text, BODY_FONT_SIZE) @@ -101,49 +107,16 @@ class Updater: return # Return to avoid further processing after action def render_wifi_screen(self): - # Title and back button - title_rect = rl.Rectangle(MARGIN + 50, MARGIN, gui_app.width - MARGIN * 2 - 100, 60) - gui_label(title_rect, "Wi-Fi Networks", 60, font_weight=FontWeight.BOLD) + # Draw the Wi-Fi manager UI + wifi_rect = rl.Rectangle(MARGIN + 50, MARGIN, gui_app.width - MARGIN * 2 - 100, gui_app.height - MARGIN * 2 - BUTTON_HEIGHT - 20) + self.wifi_manager_ui.render(wifi_rect) + back_button_rect = rl.Rectangle(MARGIN, gui_app.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) if gui_button(back_button_rect, "Back"): self.current_screen = Screen.PROMPT return # Return to avoid processing other interactions after screen change - # Draw placeholder for WiFi implementation - placeholder_rect = rl.Rectangle( - MARGIN, - title_rect.y + title_rect.height + MARGIN, - gui_app.width - MARGIN * 2, - gui_app.height - title_rect.height - MARGIN * 3 - BUTTON_HEIGHT - ) - - # Draw rounded rectangle background - rl.draw_rectangle_rounded( - placeholder_rect, - 0.1, - 10, - rl.Color(41, 41, 41, 255) - ) - - # Draw placeholder text - placeholder_text = "WiFi Implementation Placeholder" - text_size = rl.measure_text_ex(gui_app.font(), placeholder_text, 80, 1) - text_pos = rl.Vector2( - placeholder_rect.x + (placeholder_rect.width - text_size.x) / 2, - placeholder_rect.y + (placeholder_rect.height - text_size.y) / 2 - ) - rl.draw_text_ex(gui_app.font(), placeholder_text, text_pos, 80, 1, rl.WHITE) - - # Draw instructions - instructions_text = "Real WiFi functionality would be implemented here" - instructions_size = rl.measure_text_ex(gui_app.font(), instructions_text, 40, 1) - instructions_pos = rl.Vector2( - placeholder_rect.x + (placeholder_rect.width - instructions_size.x) / 2, - text_pos.y + text_size.y + 20 - ) - rl.draw_text_ex(gui_app.font(), instructions_text, instructions_pos, 40, 1, rl.GRAY) - def render_progress_screen(self): title_rect = rl.Rectangle(MARGIN + 100, 330, gui_app.width - MARGIN * 2 - 200, 100) gui_label(title_rect, self.progress_text, 90, font_weight=FontWeight.SEMI_BOLD) From e9cea3ae5cdd9b6cdfa1ca4e745224f5a7a560a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Sun, 11 May 2025 08:24:46 -0700 Subject: [PATCH 179/477] Revert TR again (#35179) Revert "Tomb Raider 7 (#35114)" This reverts commit 0fb4aafa35896e831bd58a636a9a1bb4a1442743. --- selfdrive/controls/lib/longitudinal_planner.py | 15 +++------------ selfdrive/modeld/fill_model_msg.py | 10 +++++----- selfdrive/modeld/modeld.py | 14 +++++--------- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 4 ++-- selfdrive/modeld/parse_model_outputs.py | 13 +++++++------ selfdrive/test/process_replay/ref_commit | 2 +- 7 files changed, 25 insertions(+), 37 deletions(-) diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 4219e80f33..9d71faeca2 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -90,7 +90,7 @@ class LongitudinalPlanner: return x, v, a, j, throttle_prob def update(self, sm): - self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' + self.mpc.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: accel_coast = get_coast_accel(sm['carControl'].orientationNED[1]) @@ -113,7 +113,7 @@ class LongitudinalPlanner: # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - if self.mode == 'acc': + if self.mpc.mode == 'acc': accel_clip = [ACCEL_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP) @@ -160,17 +160,8 @@ class LongitudinalPlanner: self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 action_t = self.CP.longitudinalActuatorDelay + DT_MDL - output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, + output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, action_t=action_t, vEgoStopping=self.CP.vEgoStopping) - output_a_target_e2e = sm['modelV2'].action.desiredAcceleration - output_should_stop_e2e = sm['modelV2'].action.shouldStop - - if self.mode == 'acc': - output_a_target = output_a_target_mpc - self.output_should_stop = output_should_stop_mpc - else: - output_a_target = min(output_a_target_mpc, output_a_target_e2e) - self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc for idx in range(2): accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05) diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index 36bd724d01..a91c6395c7 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -90,11 +90,11 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T) # temporal pose - #temporal_pose = modelV2.temporalPose - #temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() - #temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() - #temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() - #temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() + temporal_pose = modelV2.temporalPose + temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist() + temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist() + temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist() + temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist() # poly path fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 84d7760dce..5e1acdea0e 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -26,7 +26,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.selfdrive.modeld.constants import ModelConstants, Plan @@ -41,8 +41,8 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl' VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl' POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl' -LAT_SMOOTH_SECONDS = 0.1 -LONG_SMOOTH_SECONDS = 0.3 +LAT_SMOOTH_SECONDS = 0.0 +LONG_SMOOTH_SECONDS = 0.0 MIN_LAT_CONTROL_SPEED = 0.3 @@ -55,11 +55,7 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log. action_t=long_action_t) desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS) - desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2], - plan[:,Plan.ORIENTATION_RATE][:,2], - ModelConstants.T_IDXS, - v_ego, - lat_action_t) + desired_curvature = model_output['desired_curvature'][0, 0] if v_ego > MIN_LAT_CONTROL_SPEED: desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS) else: @@ -176,7 +172,7 @@ class ModelState: # TODO model only uses last value now self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :] - self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] + self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs] combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict} if SEND_RAW_PRED: diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 5bd77bb958..3601bbb5da 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19e30484236efff72d519938c3e26461dbeb89c11d81fa7ecbff8e0263333c18 -size 15588463 +oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36 +size 15966721 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 0a7b4a803d..aee6d8f1bf 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dad289ae367cefcb862ef1d707fb4919d008f0eeaa1ebaf18df58d8de5a7d96e -size 46265585 +oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624 +size 34882971 diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 783572d436..810c44ccb9 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -88,12 +88,6 @@ class Parser: self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,)) self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) - self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) - self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, - out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) - for k in ['lead_prob', 'lane_lines_prob']: - self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH)) self.parse_binary_crossentropy('meta', outs) return outs @@ -101,10 +95,17 @@ class Parser: def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) + self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,)) + self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION, + out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) if 'lat_planner_solution' in outs: self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) if 'desired_curvature' in outs: self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) + for k in ['lead_prob', 'lane_lines_prob']: + self.parse_binary_crossentropy(k, outs) self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,)) return outs diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 3dd3eab25f..1ae4c4a1a8 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 \ No newline at end of file +7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 From bfadab078e01bb0058de3c58069e4ea5d493198d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 12 May 2025 02:05:59 +0800 Subject: [PATCH 180/477] system/ui: set log level to WARNING to reduce noise (#35180) * set log level to WARNING to reduce noise * custom callback handling for raylib logs --- system/ui/lib/application.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index eae5962277..3f3595e8f5 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -40,6 +40,7 @@ class GuiApplication: self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False + self._trace_log_callback = None def request_close(self): self._window_close_requested = True @@ -50,6 +51,9 @@ class GuiApplication: HARDWARE.set_display_power(True) HARDWARE.set_screen_brightness(65) + self._set_log_callback() + + rl.set_trace_log_level(rl.TraceLogLevel.LOG_WARNING) rl.set_config_flags(rl.ConfigFlags.FLAG_MSAA_4X_HINT | rl.ConfigFlags.FLAG_VSYNC_HINT) rl.init_window(self._width, self._height, title) rl.set_target_fps(fps) @@ -137,6 +141,29 @@ class GuiApplication: rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(DEFAULT_TEXT_COLOR)) rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(rl.Color(50, 50, 50, 255))) + def _set_log_callback(self): + @rl.ffi.callback("void(int, char *, void *)") + def trace_log_callback(log_level, text, args): + try: + text_str = rl.ffi.string(text).decode('utf-8') + except (TypeError, UnicodeDecodeError): + text_str = str(text) + + if log_level == rl.TraceLogLevel.LOG_ERROR: + cloudlog.error(f"raylib: {text_str}") + elif log_level == rl.TraceLogLevel.LOG_WARNING: + cloudlog.warning(f"raylib: {text_str}") + elif log_level == rl.TraceLogLevel.LOG_INFO: + cloudlog.info(f"raylib: {text_str}") + elif log_level == rl.TraceLogLevel.LOG_DEBUG: + cloudlog.debug(f"raylib: {text_str}") + else: + cloudlog.debug(f"raylib: Unknown level {log_level}: {text_str}") + + # Store callback reference + self._trace_log_callback = trace_log_callback + rl.set_trace_log_callback(self._trace_log_callback) + def _monitor_fps(self): fps = rl.get_fps() From e62b6a38e9cf6431321dafefc78ea86c9a1ccf13 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 12 May 2025 02:06:04 +0800 Subject: [PATCH 181/477] system/ui: set keyboard to use full window area (#35181) set keyboard to use full window area --- system/ui/widgets/keyboard.py | 5 ++++- system/ui/widgets/network.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 13fdb912b5..d496c5f749 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,8 +1,10 @@ import pyray as rl +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.button import gui_button from openpilot.system.ui.lib.label import gui_label # Constants for special keys +CONTENT_MARGIN = 50 BACKSPACE_KEY = "<-" ENTER_KEY = "Enter" SPACE_KEY = " " @@ -55,7 +57,8 @@ class Keyboard: self._clear() return result - def render(self, rect, title, sub_title): + def render(self, title, sub_title): + rect = rl.Rectangle(CONTENT_MARGIN, CONTENT_MARGIN, gui_app.width - 2 * CONTENT_MARGIN, gui_app.height - 2 * CONTENT_MARGIN) gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90) gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY) if gui_button(rl.Rectangle(rect.x + rect.width - 300, rect.y, 300, 100), "Cancel"): diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 14f4017925..49e2dd8296 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -62,7 +62,7 @@ class WifiManagerUI: match self.state: case StateNeedsAuth(network): - result = self.keyboard.render(rect, "Enter password", f"for {network.ssid}") + result = self.keyboard.render("Enter password", f"for {network.ssid}") if result == 1: self.connect_to_network(network, self.keyboard.text) elif result == 0: From 35dbdf912478da5db58aa88a1b8e64e59f27ad3c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 11 May 2025 11:08:50 -0700 Subject: [PATCH 182/477] raylib: fwd everything to cloudlog --- system/ui/lib/application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 3f3595e8f5..c359b1e578 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -53,7 +53,7 @@ class GuiApplication: self._set_log_callback() - rl.set_trace_log_level(rl.TraceLogLevel.LOG_WARNING) + rl.set_trace_log_level(rl.TraceLogLevel.LOG_ALL) rl.set_config_flags(rl.ConfigFlags.FLAG_MSAA_4X_HINT | rl.ConfigFlags.FLAG_VSYNC_HINT) rl.init_window(self._width, self._height, title) rl.set_target_fps(fps) @@ -158,7 +158,7 @@ class GuiApplication: elif log_level == rl.TraceLogLevel.LOG_DEBUG: cloudlog.debug(f"raylib: {text_str}") else: - cloudlog.debug(f"raylib: Unknown level {log_level}: {text_str}") + cloudlog.error(f"raylib: Unknown level {log_level}: {text_str}") # Store callback reference self._trace_log_callback = trace_log_callback From 2b32325be1c32445f3e069f1536f14d4c6affc6b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 12 May 2025 22:21:12 +0800 Subject: [PATCH 183/477] system/ui: Full-Featured InputBox Component (#35178) custom input box component --- system/ui/lib/inputbox.py | 157 ++++++++++++++++++++++++++++++++++ system/ui/widgets/keyboard.py | 37 +++----- system/ui/widgets/network.py | 4 +- 3 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 system/ui/lib/inputbox.py diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py new file mode 100644 index 0000000000..9bac5251aa --- /dev/null +++ b/system/ui/lib/inputbox.py @@ -0,0 +1,157 @@ +import pyray as rl +from openpilot.system.ui.lib.application import gui_app + + +class InputBox: + def __init__(self, max_text_size=255, password_mode=False): + self._max_text_size = max_text_size + self._input_text = "" + self._cursor_position = 0 + self._password_mode = password_mode + self._blink_counter = 0 + self._show_cursor = False + self._last_key_pressed = 0 + self._key_press_time = 0 + self._repeat_delay = 30 + self._repeat_rate = 5 + + @property + def text(self): + return self._input_text + + @text.setter + def text(self, value): + self._input_text = value[: self._max_text_size] + self._cursor_position = len(self._input_text) + + def set_password_mode(self, password_mode): + self._password_mode = password_mode + + def clear(self): + self._input_text = '' + self._cursor_position = 0 + + def set_cursor_position(self, position): + """Set the cursor position and reset the blink counter.""" + if 0 <= position <= len(self._input_text): + self._cursor_position = position + self._blink_counter = 0 + self._show_cursor = True + + def add_char_at_cursor(self, char): + """Add a character at the current cursor position.""" + if len(self._input_text) < self._max_text_size: + self._input_text = self._input_text[: self._cursor_position] + char + self._input_text[self._cursor_position :] + self.set_cursor_position(self._cursor_position + 1) + return True + return False + + def delete_char_before_cursor(self): + """Delete the character before the cursor position (backspace).""" + if self._cursor_position > 0: + self._input_text = self._input_text[: self._cursor_position - 1] + self._input_text[self._cursor_position :] + self.set_cursor_position(self._cursor_position - 1) + return True + return False + + def delete_char_at_cursor(self): + """Delete the character at the cursor position (delete).""" + if self._cursor_position < len(self._input_text): + self._input_text = self._input_text[: self._cursor_position] + self._input_text[self._cursor_position + 1 :] + self.set_cursor_position(self._cursor_position) + return True + return False + + def render(self, rect, color=rl.LIGHTGRAY, border_color=rl.DARKGRAY, text_color=rl.BLACK, font_size=80): + # Handle mouse input + self._handle_mouse_input(rect, font_size) + + # Draw input box + rl.draw_rectangle_rec(rect, color) + rl.draw_rectangle_lines_ex(rect, 1, border_color) + + # Process keyboard input + self._handle_keyboard_input() + + # Update cursor blink + self._blink_counter += 1 + if self._blink_counter >= 30: + self._show_cursor = not self._show_cursor + self._blink_counter = 0 + + # Display text + font = gui_app.font() + display_text = "•" * len(self._input_text) if self._password_mode else self._input_text + padding = 10 + rl.draw_text_ex( + font, + display_text, + rl.Vector2(int(rect.x + padding), int(rect.y + rect.height / 2 - font_size / 2)), + font_size, + 0, + text_color, + ) + + # Draw cursor + if self._show_cursor: + cursor_x = rect.x + padding + if len(display_text) > 0 and self._cursor_position > 0: + cursor_x += rl.measure_text_ex(font, display_text[: self._cursor_position], font_size, 0).x + + cursor_height = font_size + 4 + cursor_y = rect.y + rect.height / 2 - cursor_height / 2 + rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.BLACK) + + def _handle_mouse_input(self, rect, font_size): + """Handle mouse clicks to position cursor.""" + mouse_pos = rl.get_mouse_position() + if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON) and rl.check_collision_point_rec(mouse_pos, rect): + # Calculate cursor position from click + if len(self._input_text) > 0: + text_width = rl.measure_text(self._input_text, font_size) + text_pos_x = rect.x + 10 + + if mouse_pos.x - text_pos_x > text_width: + self.set_cursor_position(len(self._input_text)) + else: + click_ratio = (mouse_pos.x - text_pos_x) / text_width + self.set_cursor_position(int(len(self._input_text) * click_ratio)) + + self.set_cursor_position(0) + + def _handle_keyboard_input(self): + """Process keyboard input.""" + key = rl.get_key_pressed() + + # Handle key repeats + if key == self._last_key_pressed and key != 0: + self._key_press_time += 1 + if self._key_press_time > self._repeat_delay and self._key_press_time % self._repeat_rate == 0: + # Process repeated key + pass + else: + return # Skip processing until repeat triggers + else: + self._last_key_pressed = key + self._key_press_time = 0 + + # Handle navigation keys + if key == rl.KEY_LEFT: + if self._cursor_position > 0: + self.set_cursor_position(self._cursor_position - 1) + elif key == rl.KEY_RIGHT: + if self._cursor_position < len(self._input_text): + self.set_cursor_position(self._cursor_position + 1) + elif key == rl.KEY_BACKSPACE: + self.delete_char_before_cursor() + elif key == rl.KEY_DELETE: + self.delete_char_at_cursor() + elif key == rl.KEY_HOME: + self.set_cursor_position(0) + elif key == rl.KEY_END: + self.set_cursor_position(len(self._input_text)) + + # Handle text input + char = rl.get_char_pressed() + if char != 0 and char >= 32: # Filter out control characters + self.add_char_at_cursor(chr(char)) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index d496c5f749..d81bf3090e 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,6 +1,7 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label # Constants for special keys @@ -47,28 +48,25 @@ class Keyboard: def __init__(self, max_text_size: int = 255): self._layout = keyboard_layouts["lowercase"] self._max_text_size = max_text_size - self._string_pointer = rl.ffi.new("char[]", max_text_size) - self._input_text = "" - self._clear() + self._input_box = InputBox(max_text_size) @property def text(self): - result = rl.ffi.string(self._string_pointer).decode("utf-8") - self._clear() - return result + return self._input_box.text + + def clear(self): + self._input_box.clear() def render(self, title, sub_title): rect = rl.Rectangle(CONTENT_MARGIN, CONTENT_MARGIN, gui_app.width - 2 * CONTENT_MARGIN, gui_app.height - 2 * CONTENT_MARGIN) gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90) gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY) if gui_button(rl.Rectangle(rect.x + rect.width - 300, rect.y, 300, 100), "Cancel"): - self._clear() + self.clear() return 0 # Text box for input - self._sync_string_pointer() - rl.gui_text_box(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100), self._string_pointer, self._max_text_size, True) - self._input_text = rl.ffi.string(self._string_pointer).decode("utf-8") + self._input_box.render(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100)) h_space, v_space = 15, 15 row_y_start = rect.y + 300 # Starting Y position for the first row key_height = (rect.height - 300 - 3 * v_space) / 4 @@ -104,18 +102,7 @@ class Keyboard: self._layout = keyboard_layouts["numbers"] elif key == SYMBOL_KEY: self._layout = keyboard_layouts["specials"] - elif key == BACKSPACE_KEY and len(self._input_text) > 0: - self._input_text = self._input_text[:-1] - elif key != BACKSPACE_KEY and len(self._input_text) < self._max_text_size: - self._input_text += key - - def _clear(self): - self._input_text = '' - self._string_pointer[0] = b'\0' - - def _sync_string_pointer(self): - """Sync the C-string pointer with the internal Python string.""" - encoded = self._input_text.encode("utf-8")[:self._max_text_size - 1] # Leave room for the null terminator - buffer = rl.ffi.buffer(self._string_pointer) - buffer[:len(encoded)] = encoded - self._string_pointer[len(encoded)] = b'\0' # Null terminator + elif key == BACKSPACE_KEY: + self._input_box.delete_char_before_cursor() + else: + self._input_box.add_char_at_cursor(key) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 49e2dd8296..a265111a46 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -64,7 +64,9 @@ class WifiManagerUI: case StateNeedsAuth(network): result = self.keyboard.render("Enter password", f"for {network.ssid}") if result == 1: - self.connect_to_network(network, self.keyboard.text) + password = self.keyboard.text + self.keyboard.clear() + self.connect_to_network(network, password) elif result == 0: self.state = StateIdle() From 59fbeb60707e3a3cb16064dda308260193e00a95 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 12 May 2025 15:28:36 +0100 Subject: [PATCH 184/477] system/ui: keyboard demo script --- system/ui/widgets/keyboard.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index d81bf3090e..0723e65a16 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -57,7 +57,7 @@ class Keyboard: def clear(self): self._input_box.clear() - def render(self, title, sub_title): + def render(self, title: str, sub_title: str): rect = rl.Rectangle(CONTENT_MARGIN, CONTENT_MARGIN, gui_app.width - 2 * CONTENT_MARGIN, gui_app.height - 2 * CONTENT_MARGIN) gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90) gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY) @@ -106,3 +106,17 @@ class Keyboard: self._input_box.delete_char_before_cursor() else: self._input_box.add_char_at_cursor(key) + + +if __name__ == "__main__": + gui_app.init_window("Keyboard") + keyboard = Keyboard() + for _ in gui_app.render(): + result = keyboard.render("Keyboard", "Type here") + if result == 1: + print(f"You typed: {keyboard.text}") + gui_app.request_close() + elif result == 0: + print("Canceled") + gui_app.request_close() + gui_app.close() From 43054a56c263ad84978e9cfb6cc05d92e0a212ee Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 12 May 2025 15:39:48 +0100 Subject: [PATCH 185/477] system/ui: fix tethering ssid (#35188) system/ui: fix tethering ssid when no params --- system/ui/lib/wifi_manager.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 00649b146b..e08e4cbb62 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -79,7 +79,11 @@ class WifiManager: self.saved_connections: dict[str, str] = {} self.active_ap_path: str = "" self.scan_task: asyncio.Task | None = None - self._tethering_ssid = "weedle-" + Params().get("DongleId", encoding="utf-8") + # Set tethering ssid as "weedle" + first 4 characters of a dongle id + self._tethering_ssid = "weedle" + dongle_id = Params().get("DongleId", encoding="utf-8") + if dongle_id: + self._tethering_ssid += "-" + dongle_id[:4] self.running: bool = True self._current_connection_ssid: str | None = None From 02125f68629153f2cd7827fab64c011821d0ea10 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 12 May 2025 23:59:00 +0800 Subject: [PATCH 186/477] system/ui: fix cursor position in inputBox mouse click handler (#35189) fix cursor position in inputBox mouse click handler --- system/ui/lib/inputbox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 9bac5251aa..d933d42476 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -108,7 +108,7 @@ class InputBox: if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON) and rl.check_collision_point_rec(mouse_pos, rect): # Calculate cursor position from click if len(self._input_text) > 0: - text_width = rl.measure_text(self._input_text, font_size) + text_width = rl.measure_text_ex(gui_app.font(), self._input_text, font_size, 0).x text_pos_x = rect.x + 10 if mouse_pos.x - text_pos_x > text_width: @@ -116,7 +116,7 @@ class InputBox: else: click_ratio = (mouse_pos.x - text_pos_x) / text_width self.set_cursor_position(int(len(self._input_text) * click_ratio)) - + else: self.set_cursor_position(0) def _handle_keyboard_input(self): From 98dba943b2519c322e28a0d636cbbbf67c930d51 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 12 May 2025 09:30:01 -0700 Subject: [PATCH 187/477] feat(clip): add timestamp onto clip (#35182) * feat(clip): add timestamp onto clip * same font size as title * revert * just declare it here, and add comments --- tools/clip/run.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 33c98a0213..12ac879baa 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -165,12 +165,16 @@ def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, rou # TODO: evaluate creating fn that inspects /tmp/.X11-unix and creates unused display to avoid possibility of collision display = f':{randint(99, 999)}' + box_style = 'box=1:boxcolor=black@0.33:boxborderw=7' meta_text = get_meta_text(route) overlays = [ - f"drawtext=text='{escape_ffmpeg_text(meta_text)}':fontfile=Inter.tff:fontcolor=white:fontsize=18:box=1:boxcolor=black@0.33:boxborderw=7:x=(w-text_w)/2:y=5.5:enable='between(t,1,5)'" + # metadata overlay + f"drawtext=text='{escape_ffmpeg_text(meta_text)}':fontfile=Inter.tff:fontcolor=white:fontsize=18:{box_style}:x=(w-text_w)/2:y=5.5:enable='between(t,1,5)'", + # route time overlay + f"drawtext=text='%{{eif\\:floor(({start}+t)/60)\\:d\\:2}}\\:%{{eif\\:mod({start}+t\\,60)\\:d\\:2}}':fontfile=Inter.tff:fontcolor=white:fontsize=24:{box_style}:x=w-text_w-38:y=38" ] if title: - overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile=Inter.tff:fontcolor=white:fontsize=32:box=1:boxcolor=black@0.33:boxborderw=10:x=(w-text_w)/2:y=53") + overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile=Inter.tff:fontcolor=white:fontsize=32:{box_style}:x=(w-text_w)/2:y=53") ffmpeg_cmd = [ 'ffmpeg', '-y', '-video_size', RESOLUTION, '-framerate', str(FRAMERATE), '-f', 'x11grab', '-draw_mouse', '0', From 8c995ab26dbdba821b5fad17cd4d29dec676d314 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 13 May 2025 00:32:20 +0800 Subject: [PATCH 188/477] system/ui: implement CameraView widget (#35185) * initial commit * run at 60hz, nonblock * graceful cleanup * re-position * rename --------- Co-authored-by: Cameron Clough --- system/ui/widgets/cameraview.py | 87 +++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 system/ui/widgets/cameraview.py diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py new file mode 100644 index 0000000000..841440ae3a --- /dev/null +++ b/system/ui/widgets/cameraview.py @@ -0,0 +1,87 @@ +import pyray as rl +from msgq.visionipc import VisionIpcClient, VisionStreamType +from openpilot.system.ui.lib.application import gui_app + +FRAME_FRAGMENT_SHADER = """ +#version 330 core +in vec2 fragTexCoord; uniform sampler2D texture0, texture1; out vec4 fragColor; +void main() { + float y = texture(texture0, fragTexCoord).r; + vec2 uv = texture(texture1, fragTexCoord).ra - 0.5; + fragColor = vec4(y + 1.402*uv.y, y - 0.344*uv.x - 0.714*uv.y, y + 1.772*uv.x, 1.0); +}""" + + +class CameraView: + def __init__(self, name: str, stream_type: VisionStreamType): + self.client = VisionIpcClient(name, stream_type, False) + self.shader = rl.load_shader_from_memory(rl.ffi.NULL, FRAME_FRAGMENT_SHADER) + self.texture_y: rl.Texture | None = None + self.texture_uv: rl.Texture | None = None + self.frame = None + + def close(self): + self._clear_textures() + if self.shader and self.shader.id: + rl.unload_shader(self.shader) + + def render(self, rect: rl.Rectangle): + if not self._ensure_connection(): + return + + buffer = self.client.recv(timeout_ms=0) + self.frame = buffer if buffer else self.frame + if not self.frame or not self.texture_y or not self.texture_uv: + return + + y_data = self.frame.data[: self.frame.uv_offset] + uv_data = self.frame.data[self.frame.uv_offset :] + + rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data)) + rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data)) + + # Calculate scaling to maintain aspect ratio + scale = min(rect.width / self.frame.width, rect.height / self.frame.height) + x_offset = rect.x + (rect.width - (self.frame.width * scale)) / 2 + y_offset = rect.y + (rect.height - (self.frame.height * scale)) / 2 + src_rect = rl.Rectangle(0, 0, float(self.frame.width), float(self.frame.height)) + dst_rect = rl.Rectangle(x_offset, y_offset, self.frame.width * scale, self.frame.height * scale) + + rl.begin_shader_mode(self.shader) + rl.set_shader_value_texture(self.shader, rl.get_shader_location(self.shader, "texture1"), self.texture_uv) + rl.draw_texture_pro(self.texture_y, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) + rl.end_shader_mode() + + def _ensure_connection(self) -> bool: + if not self.client.is_connected(): + self.frame = None + if not self.client.connect(False) or not self.client.num_buffers: + return False + + self._clear_textures() + self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride), + int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)) + self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), + int(self.client.height // 2), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)) + return True + + def _clear_textures(self): + if self.texture_y and self.texture_y.id: + rl.unload_texture(self.texture_y) + if self.texture_uv and self.texture_uv.id: + rl.unload_texture(self.texture_uv) + +if __name__ == "__main__": + gui_app.init_window("watch3") + road_camera_view = CameraView("camerad", VisionStreamType.VISION_STREAM_ROAD) + driver_camera_view = CameraView("camerad", VisionStreamType.VISION_STREAM_DRIVER) + wide_road_camera_view = CameraView("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD) + try: + for _ in gui_app.render(): + road_camera_view.render(rl.Rectangle(gui_app.width // 4, 0, gui_app.width // 2, gui_app.height // 2)) + driver_camera_view.render(rl.Rectangle(0, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) + wide_road_camera_view.render(rl.Rectangle(gui_app.width // 2, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) + finally: + road_camera_view.close() + driver_camera_view.close() + wide_road_camera_view.close() From ce4fda1f92cd4d1cccfe4e9ec123ce3a5068d96e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 13 May 2025 02:01:25 +0800 Subject: [PATCH 189/477] system/ui: add minimum WIFI password length validation (#35190) * add minimum WIFI password length validation * add min text size to keyboard * disable enter if text size= self._min_text_size + if gui_button(key_rect, key, is_enabled=is_enabled): if key == ENTER_KEY: return 1 else: @@ -110,7 +112,7 @@ class Keyboard: if __name__ == "__main__": gui_app.init_window("Keyboard") - keyboard = Keyboard() + keyboard = Keyboard(min_text_size=8) for _ in gui_app.render(): result = keyboard.render("Keyboard", "Type here") if result == 1: diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index a265111a46..06448483aa 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -11,6 +11,8 @@ from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog NM_DEVICE_STATE_NEED_AUTH = 60 +MIN_PASSWORD_LENGTH = 8 +MAX_PASSWORD_LENGTH = 64 ITEM_HEIGHT = 160 @@ -46,7 +48,7 @@ class WifiManagerUI: self.state: UIState = StateIdle() self.btn_width = 200 self.scroll_panel = GuiScrollPanel() - self.keyboard = Keyboard() + self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH) self._networks: list[NetworkInfo] = [] @@ -66,7 +68,9 @@ class WifiManagerUI: if result == 1: password = self.keyboard.text self.keyboard.clear() - self.connect_to_network(network, password) + + if len(password) >= MIN_PASSWORD_LENGTH: + self.connect_to_network(network, password) elif result == 0: self.state = StateIdle() From b2d414cc8ba3db7d534d676084704a40e735aa26 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 12 May 2025 13:01:29 -0700 Subject: [PATCH 190/477] [bot] Update Python packages (#35187) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- uv.lock | 1304 +++++++++++++++++++++++++------------------------- 2 files changed, 655 insertions(+), 651 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index c856a2c0bd..b922517802 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c856a2c0bd2b3c75f86a73b051c0c4cc7159559e +Subproject commit b9225178021be2b606dab89bf941440562f4cc6c diff --git a/uv.lock b/uv.lock index fbeda4d552..60e0e521c5 100644 --- a/uv.lock +++ b/uv.lock @@ -163,7 +163,7 @@ wheels = [ [[package]] name = "azure-identity" -version = "1.21.0" +version = "1.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -172,9 +172,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/a1/f1a683672e7a88ea0e3119f57b6c7843ed52650fdcac8bfa66ed84e86e40/azure_identity-1.21.0.tar.gz", hash = "sha256:ea22ce6e6b0f429bc1b8d9212d5b9f9877bd4c82f1724bfa910760612c07a9a6", size = 266445, upload-time = "2025-03-11T20:53:07.463Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/8e/1b5916f5e1696bf05b009cf7d41383cea54aa8536d4a4f6f88cca15eb6a4/azure_identity-1.22.0.tar.gz", hash = "sha256:c8f5ef23e5295c2fa300c984dd9f5e1fe43503fc25c121c37ff6a15e39b800b9", size = 263346, upload-time = "2025-05-06T20:22:24.13Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/9f/1f9f3ef4f49729ee207a712a5971a9ca747f2ca47d9cbf13cf6953e3478a/azure_identity-1.21.0-py3-none-any.whl", hash = "sha256:258ea6325537352440f71b35c3dffe9d240eae4a5126c1b7ce5efd5766bd9fd9", size = 189190, upload-time = "2025-03-11T20:53:09.197Z" }, + { url = "https://files.pythonhosted.org/packages/06/1a/6f13d7f95f68f37303c0e00e011d498e4524e70d354b2e11ef5ae89e0ce0/azure_identity-1.22.0-py3-none-any.whl", hash = "sha256:26d6c63f2ca453c77c3e74be8613941ad074e05d0c8be135247573752c249ad8", size = 185524, upload-time = "2025-05-06T20:22:25.991Z" }, ] [[package]] @@ -295,14 +295,14 @@ wheels = [ [[package]] name = "click" -version = "8.1.8" +version = "8.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/0f/62ca20172d4f87d93cf89665fbaedcd560ac48b465bd1d92bfc7ea6b0a41/click-8.2.0.tar.gz", hash = "sha256:f5452aeddd9988eefa20f90f05ab66f17fce1ee2a36907fd30b05bbb5953814d", size = 235857, upload-time = "2025-05-10T22:21:03.111Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, + { url = "https://files.pythonhosted.org/packages/a2/58/1f37bf81e3c689cc74ffa42102fa8915b59085f54a6e4a80bc6265c0f6bf/click-8.2.0-py3-none-any.whl", hash = "sha256:6b303f0b2aa85f1cb4e5303078fadcbcd4e476f114fab9b5007005711839325c", size = 102156, upload-time = "2025-05-10T22:21:01.352Z" }, ] [[package]] @@ -447,27 +447,31 @@ wheels = [ [[package]] name = "cython" -version = "3.0.12" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617, upload-time = "2025-02-11T09:05:50.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/f7/db37a613aec5abcd51c8000a386a701ac32e94659aa03fa69c3e5c19b149/cython-3.1.0.tar.gz", hash = "sha256:1097dd60d43ad0fff614a57524bfd531b35c13a907d13bee2cc2ec152e6bf4a1", size = 3181017, upload-time = "2025-05-08T20:25:36.12Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430, upload-time = "2025-02-11T09:06:47.253Z" }, - { url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055, upload-time = "2025-02-11T09:06:50.807Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874, upload-time = "2025-02-11T09:06:54.806Z" }, - { url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129, upload-time = "2025-02-11T09:06:58.152Z" }, - { url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936, upload-time = "2025-02-11T09:07:01.592Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066, upload-time = "2025-02-11T09:07:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935, upload-time = "2025-02-11T09:07:06.947Z" }, - { url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337, upload-time = "2025-02-11T09:07:10.087Z" }, - { url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717, upload-time = "2025-02-11T09:07:12.405Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337, upload-time = "2025-02-11T09:07:14.979Z" }, - { url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692, upload-time = "2025-02-11T09:07:17.45Z" }, - { url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057, upload-time = "2025-02-11T09:07:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493, upload-time = "2025-02-11T09:07:25.183Z" }, - { url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859, upload-time = "2025-02-11T09:07:27.634Z" }, - { url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428, upload-time = "2025-02-11T09:07:30.719Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755, upload-time = "2025-02-11T09:07:36.021Z" }, - { url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640, upload-time = "2025-02-11T09:05:45.648Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/eb20821704e0bac5051ae552db574cf5022ba658006a0c187d85d50cfd69/cython-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c736405078dc376502617eb41c39e223ae176ebd1a4ddc18179d2517bc8c8658", size = 3004811, upload-time = "2025-05-08T21:20:38.179Z" }, + { url = "https://files.pythonhosted.org/packages/59/6f/2f4e1382e365006055ec7b803372283065c4cef6f5bab88f8ed2ab350452/cython-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1215d3adb4e8691d03e712aed31206d21f387a8003d8de6a574ee75fe6d2e07c", size = 2868305, upload-time = "2025-05-08T21:20:41.1Z" }, + { url = "https://files.pythonhosted.org/packages/6e/b3/9778e21b317c10e9a1028270eb87eda91202cd37ee1e0c09630a600629ef/cython-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:522d4dae1fea71eee5c944fb7a8530de8bdd6df0ccb2bd001d0f75be228eac6c", size = 3130151, upload-time = "2025-05-08T21:20:44.436Z" }, + { url = "https://files.pythonhosted.org/packages/58/e7/4fe8304d1caccff52a4c042274b42bf23574a380e64a86daaf4c695be510/cython-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462ad6142057e81715ada74e2d24b9a07bf36ae3da72bf973478b5c3e809c26d", size = 3220382, upload-time = "2025-05-08T21:20:47.232Z" }, + { url = "https://files.pythonhosted.org/packages/0a/88/9d3d7a1cb8ce3e93acbabac6f2275cfaa1021c2ddf93d2c0999bb2ee4d43/cython-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8f00cdeb14f004ebeacf946e06bad2e3ed5776af96f5af95f92d822c4ba275f", size = 3287586, upload-time = "2025-05-08T21:20:49.72Z" }, + { url = "https://files.pythonhosted.org/packages/91/49/99148b55ad1c036fb1cb69bf3729d2beecc4246f7c5f487006f16ec1f394/cython-3.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37d62b8b8919126c75769e5470b288d76c83a1645e73c7aca4b7d7aecb3c1234", size = 3175407, upload-time = "2025-05-08T21:20:51.819Z" }, + { url = "https://files.pythonhosted.org/packages/0e/52/297ce50c21647ae3741856e57f04ba69b1819e5658756823a3a1efd05e8e/cython-3.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bea0b6bfde7493acb0529fc603abd4b3b13c3bb2fff7a889ae5a8d3ea7dc5a84", size = 3381904, upload-time = "2025-05-08T21:20:53.941Z" }, + { url = "https://files.pythonhosted.org/packages/61/ab/2966d2786f4e6a7903d23b019a810675334960e8d88686a985286bd8f53d/cython-3.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fe8c1db9ec03d9ef83e33c842c108e892577ade4c5f530c9435beced048e4698", size = 3305998, upload-time = "2025-05-08T21:20:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/62/52/0670d10b563b2f345477a158bd133399ddb9e7f6ff16e397e77932c07026/cython-3.1.0-cp311-cp311-win32.whl", hash = "sha256:5f6417d378bd11ca55f16e3c1c7c3bf6d7f0a0cc326c46e309fcba46c54ba4f1", size = 2454005, upload-time = "2025-05-08T21:20:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/53/6e/3ea341450bc40f3c0c3da9ae05c22eeb59930531accba78a4b3eebaf342c/cython-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:dde3726aa5acbe879f849a09606b886491f950cfa993b435e50e9561fdf731c6", size = 2663258, upload-time = "2025-05-08T21:21:00.903Z" }, + { url = "https://files.pythonhosted.org/packages/e9/64/ae1d8848550ec3975634fcf189ccc85e73c3b9f76369dd85c484f2f8f1c3/cython-3.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8f8c4753f6b926046c0cdf6037ba8560f6677730bf0ab9c1db4e0163b4bb30f9", size = 3012737, upload-time = "2025-05-08T21:21:06.024Z" }, + { url = "https://files.pythonhosted.org/packages/cc/e6/c8fe5e71b4272a1b5683a006bfe4bfeb5e2691c6603b44d18eb57772f626/cython-3.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db8e15c8eeee529468eab08528c9bf714a94354b34375be6c0c110f6012a4768", size = 2843774, upload-time = "2025-05-08T21:21:08.112Z" }, + { url = "https://files.pythonhosted.org/packages/6a/eb/aeb62d32049ab55555e679b7d0cdb2f8c16f6a9d1189a56a96a0c1e86083/cython-3.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a46b34defa672268474fbb5541f6297f45df9e4ecc4def6edd6fe1c44bfdb795", size = 3080908, upload-time = "2025-05-08T21:21:10.228Z" }, + { url = "https://files.pythonhosted.org/packages/c9/7a/5d5507f1fafd3215ea7d01e1c78809ca71dddd88b4ce75dba3ca53ab2b3b/cython-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8818446612461aca3978ebe8e3def817a120d91f85022540843ebe4f24818cd6", size = 3197585, upload-time = "2025-05-08T21:21:12.354Z" }, + { url = "https://files.pythonhosted.org/packages/f8/97/34dd0112d042ec85736273acb2caced7e576681fbf003bcefd007bbd36d2/cython-3.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe401e825b0fbeec75f8cc758c8cf32345c673bdb0edaf9585cd43b9d2798824", size = 3244153, upload-time = "2025-05-08T21:21:14.692Z" }, + { url = "https://files.pythonhosted.org/packages/fb/98/77c50a153f4acdf7b48d055c5387edbcd3de1e0d17affca5181cb009f702/cython-3.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c96908b302e87e99915b3b66481a976e32b864e95bf054dcd2cb859dffd8cb10", size = 3123440, upload-time = "2025-05-08T21:21:16.873Z" }, + { url = "https://files.pythonhosted.org/packages/63/22/ecb9301ee1efbb0b3a6be371fd01be87d45113ed2fe804897d399da8af6f/cython-3.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cdde5f25fdb8a5d50dbe5d418fe5bfb2260b1acdbd45b788e77b247e9adf2f56", size = 3337569, upload-time = "2025-05-08T21:21:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/fa/67/b707c2548cc2f790c60bb634b996f93e748ad081681c28fe7a22591e663e/cython-3.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe3320d13cde70fa8b1936e633b9e0fa68720cc61f97aa371d56d0f84fba3e02", size = 3289049, upload-time = "2025-05-08T21:21:21.997Z" }, + { url = "https://files.pythonhosted.org/packages/be/61/05b58fc96248b2d6e487f1f8d20217d3ce6d4ba5b621fb315b23c87d077c/cython-3.1.0-cp312-cp312-win32.whl", hash = "sha256:d41d17d7cfcfbddf3b7dc0ceddb6361b8e749b0b3c5f8efa40c31c249127fa15", size = 2469164, upload-time = "2025-05-08T21:21:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a1/74009b116dbec7d6c721ac268a4aa2c0c0069c99c138586f27c8fb77cae3/cython-3.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:61eb67401bd6c977084fc789812bd40f96be500049adb2bab99921d696ae0c87", size = 2673707, upload-time = "2025-05-08T21:21:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cc/221af506254978b119a2f6769b81b16bcfe09e0fb3fc5ab66e53e536d933/cython-3.1.0-py3-none-any.whl", hash = "sha256:4e460bdf1d8742ddf4914959842f2f23ca4934df97f864be799ddf1912acd0ab", size = 1227398, upload-time = "2025-05-08T20:25:31.368Z" }, ] [[package]] @@ -503,7 +507,7 @@ version = "0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "python-xlib", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/2f/3a/46ca34abf0725a754bc44ef474ad34aedcc3ea23b052d97b18b76715a6a9/EWMHlib-0.2-py3-none-any.whl", hash = "sha256:f5b07d8cfd4c7734462ee744c32d490f2f3233fa7ab354240069344208d2f6f5", size = 46657, upload-time = "2024-04-17T08:15:56.338Z" }, @@ -538,27 +542,27 @@ wheels = [ [[package]] name = "fonttools" -version = "4.57.0" +version = "4.58.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/2d/a9a0b6e3a0cf6bd502e64fc16d894269011930cabfc89aee20d1635b1441/fonttools-4.57.0.tar.gz", hash = "sha256:727ece10e065be2f9dd239d15dd5d60a66e17eac11aea47d447f9f03fdbc42de", size = 3492448, upload-time = "2025-04-03T11:07:13.898Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/cf/4d037663e2a1fe30fddb655d755d76e18624be44ad467c07412c2319ab97/fonttools-4.58.0.tar.gz", hash = "sha256:27423d0606a2c7b336913254bf0b1193ebd471d5f725d665e875c5e88a011a43", size = 3514522, upload-time = "2025-05-10T17:36:35.886Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/1f/e67c99aa3c6d3d2f93d956627e62a57ae0d35dc42f26611ea2a91053f6d6/fonttools-4.57.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3871349303bdec958360eedb619169a779956503ffb4543bb3e6211e09b647c4", size = 2757392, upload-time = "2025-04-03T11:05:45.715Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f1/f75770d0ddc67db504850898d96d75adde238c35313409bfcd8db4e4a5fe/fonttools-4.57.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c59375e85126b15a90fcba3443eaac58f3073ba091f02410eaa286da9ad80ed8", size = 2285609, upload-time = "2025-04-03T11:05:47.977Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d3/bc34e4953cb204bae0c50b527307dce559b810e624a733351a654cfc318e/fonttools-4.57.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967b65232e104f4b0f6370a62eb33089e00024f2ce143aecbf9755649421c683", size = 4873292, upload-time = "2025-04-03T11:05:49.921Z" }, - { url = "https://files.pythonhosted.org/packages/41/b8/d5933559303a4ab18c799105f4c91ee0318cc95db4a2a09e300116625e7a/fonttools-4.57.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39acf68abdfc74e19de7485f8f7396fa4d2418efea239b7061d6ed6a2510c746", size = 4902503, upload-time = "2025-04-03T11:05:52.17Z" }, - { url = "https://files.pythonhosted.org/packages/32/13/acb36bfaa316f481153ce78de1fa3926a8bad42162caa3b049e1afe2408b/fonttools-4.57.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d077f909f2343daf4495ba22bb0e23b62886e8ec7c109ee8234bdbd678cf344", size = 5077351, upload-time = "2025-04-03T11:05:54.162Z" }, - { url = "https://files.pythonhosted.org/packages/b5/23/6d383a2ca83b7516d73975d8cca9d81a01acdcaa5e4db8579e4f3de78518/fonttools-4.57.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:46370ac47a1e91895d40e9ad48effbe8e9d9db1a4b80888095bc00e7beaa042f", size = 5275067, upload-time = "2025-04-03T11:05:57.375Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ca/31b8919c6da0198d5d522f1d26c980201378c087bdd733a359a1e7485769/fonttools-4.57.0-cp311-cp311-win32.whl", hash = "sha256:ca2aed95855506b7ae94e8f1f6217b7673c929e4f4f1217bcaa236253055cb36", size = 2158263, upload-time = "2025-04-03T11:05:59.567Z" }, - { url = "https://files.pythonhosted.org/packages/13/4c/de2612ea2216eb45cfc8eb91a8501615dd87716feaf5f8fb65cbca576289/fonttools-4.57.0-cp311-cp311-win_amd64.whl", hash = "sha256:17168a4670bbe3775f3f3f72d23ee786bd965395381dfbb70111e25e81505b9d", size = 2204968, upload-time = "2025-04-03T11:06:02.16Z" }, - { url = "https://files.pythonhosted.org/packages/cb/98/d4bc42d43392982eecaaca117d79845734d675219680cd43070bb001bc1f/fonttools-4.57.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:889e45e976c74abc7256d3064aa7c1295aa283c6bb19810b9f8b604dfe5c7f31", size = 2751824, upload-time = "2025-04-03T11:06:03.782Z" }, - { url = "https://files.pythonhosted.org/packages/1a/62/7168030eeca3742fecf45f31e63b5ef48969fa230a672216b805f1d61548/fonttools-4.57.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0425c2e052a5f1516c94e5855dbda706ae5a768631e9fcc34e57d074d1b65b92", size = 2283072, upload-time = "2025-04-03T11:06:05.533Z" }, - { url = "https://files.pythonhosted.org/packages/5d/82/121a26d9646f0986ddb35fbbaf58ef791c25b59ecb63ffea2aab0099044f/fonttools-4.57.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44c26a311be2ac130f40a96769264809d3b0cb297518669db437d1cc82974888", size = 4788020, upload-time = "2025-04-03T11:06:07.249Z" }, - { url = "https://files.pythonhosted.org/packages/5b/26/e0f2fb662e022d565bbe280a3cfe6dafdaabf58889ff86fdef2d31ff1dde/fonttools-4.57.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c41ba992df5b8d680b89fd84c6a1f2aca2b9f1ae8a67400c8930cd4ea115f6", size = 4859096, upload-time = "2025-04-03T11:06:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/9e/44/9075e323347b1891cdece4b3f10a3b84a8f4c42a7684077429d9ce842056/fonttools-4.57.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea1e9e43ca56b0c12440a7c689b1350066595bebcaa83baad05b8b2675129d98", size = 4964356, upload-time = "2025-04-03T11:06:11.294Z" }, - { url = "https://files.pythonhosted.org/packages/48/28/caa8df32743462fb966be6de6a79d7f30393859636d7732e82efa09fbbb4/fonttools-4.57.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84fd56c78d431606332a0627c16e2a63d243d0d8b05521257d77c6529abe14d8", size = 5226546, upload-time = "2025-04-03T11:06:13.6Z" }, - { url = "https://files.pythonhosted.org/packages/f6/46/95ab0f0d2e33c5b1a4fc1c0efe5e286ba9359602c0a9907adb1faca44175/fonttools-4.57.0-cp312-cp312-win32.whl", hash = "sha256:f4376819c1c778d59e0a31db5dc6ede854e9edf28bbfa5b756604727f7f800ac", size = 2146776, upload-time = "2025-04-03T11:06:15.643Z" }, - { url = "https://files.pythonhosted.org/packages/06/5d/1be5424bb305880e1113631f49a55ea7c7da3a5fe02608ca7c16a03a21da/fonttools-4.57.0-cp312-cp312-win_amd64.whl", hash = "sha256:57e30241524879ea10cdf79c737037221f77cc126a8cdc8ff2c94d4a522504b9", size = 2193956, upload-time = "2025-04-03T11:06:17.534Z" }, - { url = "https://files.pythonhosted.org/packages/90/27/45f8957c3132917f91aaa56b700bcfc2396be1253f685bd5c68529b6f610/fonttools-4.57.0-py3-none-any.whl", hash = "sha256:3122c604a675513c68bd24c6a8f9091f1c2376d18e8f5fe5a101746c81b3e98f", size = 1093605, upload-time = "2025-04-03T11:07:11.341Z" }, + { url = "https://files.pythonhosted.org/packages/76/2e/9b9bd943872a50cb182382f8f4a99af92d76e800603d5f73e4343fdce61a/fonttools-4.58.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9345b1bb994476d6034996b31891c0c728c1059c05daa59f9ab57d2a4dce0f84", size = 2751920, upload-time = "2025-05-10T17:35:16.487Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8c/e8d6375da893125f610826c2e30e6d2597dfb8dad256f8ff5a54f3089fda/fonttools-4.58.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1d93119ace1e2d39ff1340deb71097932f72b21c054bd3da727a3859825e24e5", size = 2313957, upload-time = "2025-05-10T17:35:18.906Z" }, + { url = "https://files.pythonhosted.org/packages/4f/1b/a29cb00c8c20164b24f88780e298fafd0bbfb25cf8bc7b10c4b69331ad5d/fonttools-4.58.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79c9e4f01bb04f19df272ae35314eb6349fdb2e9497a163cd22a21be999694bd", size = 4913808, upload-time = "2025-05-10T17:35:21.394Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ab/9b9507b65b15190cbfe1ccd3c08067d79268d8312ef20948b16d9f5aa905/fonttools-4.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62ecda1465d38248aaf9bee1c17a21cf0b16aef7d121d7d303dbb320a6fd49c2", size = 4935876, upload-time = "2025-05-10T17:35:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/15/e4/1395853bc775b0ab06a1c61cf261779afda7baff3f65cf1197bbd21aa149/fonttools-4.58.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29d0499bff12a26733c05c1bfd07e68465158201624b2fba4a40b23d96c43f94", size = 4974798, upload-time = "2025-05-10T17:35:26.189Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b9/0358368ef5462f4653a198207b29885bee8d5e23c870f6125450ed88e693/fonttools-4.58.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1871abdb0af582e2d96cc12d88889e3bfa796928f491ec14d34a2e58ca298c7e", size = 5093560, upload-time = "2025-05-10T17:35:28.577Z" }, + { url = "https://files.pythonhosted.org/packages/11/00/f64bc3659980c41eccf2c371e62eb15b40858f02a41a0e9c6258ef094388/fonttools-4.58.0-cp311-cp311-win32.whl", hash = "sha256:e292485d70402093eb94f6ab7669221743838b8bd4c1f45c84ca76b63338e7bf", size = 2186330, upload-time = "2025-05-10T17:35:31.733Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a0/0287be13a1ec7733abf292ffbd76417cea78752d4ce10fecf92d8b1252d6/fonttools-4.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:6df3755fcf9ad70a74ad3134bd5c9738f73c9bb701a304b1c809877b11fe701c", size = 2234687, upload-time = "2025-05-10T17:35:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4e/1c6b35ec7c04d739df4cf5aace4b7ec284d6af2533a65de21972e2f237d9/fonttools-4.58.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:aa8316798f982c751d71f0025b372151ea36405733b62d0d94d5e7b8dd674fa6", size = 2737502, upload-time = "2025-05-10T17:35:36.436Z" }, + { url = "https://files.pythonhosted.org/packages/fc/72/c6fcafa3c9ed2b69991ae25a1ba7a3fec8bf74928a96e8229c37faa8eda2/fonttools-4.58.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c6db489511e867633b859b11aefe1b7c0d90281c5bdb903413edbb2ba77b97f1", size = 2307214, upload-time = "2025-05-10T17:35:38.939Z" }, + { url = "https://files.pythonhosted.org/packages/52/11/1015cedc9878da6d8d1758049749eef857b693e5828d477287a959c8650f/fonttools-4.58.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:107bdb2dacb1f627db3c4b77fb16d065a10fe88978d02b4fc327b9ecf8a62060", size = 4811136, upload-time = "2025-05-10T17:35:41.491Z" }, + { url = "https://files.pythonhosted.org/packages/32/b9/6a1bc1af6ec17eead5d32e87075e22d0dab001eace0b5a1542d38c6a9483/fonttools-4.58.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba7212068ab20f1128a0475f169068ba8e5b6e35a39ba1980b9f53f6ac9720ac", size = 4876598, upload-time = "2025-05-10T17:35:43.986Z" }, + { url = "https://files.pythonhosted.org/packages/d8/46/b14584c7ea65ad1609fb9632251016cda8a2cd66b15606753b9f888d3677/fonttools-4.58.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f95ea3b6a3b9962da3c82db73f46d6a6845a6c3f3f968f5293b3ac1864e771c2", size = 4872256, upload-time = "2025-05-10T17:35:46.617Z" }, + { url = "https://files.pythonhosted.org/packages/05/78/b2105a7812ca4ef9bf180cd741c82f4522316c652ce2a56f788e2eb54b62/fonttools-4.58.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:874f1225cc4ccfeac32009887f722d7f8b107ca5e867dcee067597eef9d4c80b", size = 5028710, upload-time = "2025-05-10T17:35:49.227Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a9/a38c85ffd30d1f2c7a5460c8abfd1aa66e00c198df3ff0b08117f5c6fcd9/fonttools-4.58.0-cp312-cp312-win32.whl", hash = "sha256:5f3cde64ec99c43260e2e6c4fa70dfb0a5e2c1c1d27a4f4fe4618c16f6c9ff71", size = 2173593, upload-time = "2025-05-10T17:35:51.226Z" }, + { url = "https://files.pythonhosted.org/packages/66/48/29752962a74b7ed95da976b5a968bba1fe611a4a7e50b9fefa345e6e7025/fonttools-4.58.0-cp312-cp312-win_amd64.whl", hash = "sha256:2aee08e2818de45067109a207cbd1b3072939f77751ef05904d506111df5d824", size = 2223230, upload-time = "2025-05-10T17:35:53.653Z" }, + { url = "https://files.pythonhosted.org/packages/9b/1f/4417c26e26a1feab85a27e927f7a73d8aabc84544be8ba108ce4aa90eb1e/fonttools-4.58.0-py3-none-any.whl", hash = "sha256:c96c36880be2268be409df7b08c5b5dacac1827083461a6bc2cb07b8cbcec1d7", size = 1111440, upload-time = "2025-05-10T17:36:33.607Z" }, ] [[package]] @@ -650,10 +654,10 @@ name = "gymnasium" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cloudpickle", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "farama-notifications", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "cloudpickle" }, + { name = "farama-notifications" }, + { name = "numpy" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/69/70cd29e9fc4953d013b15981ee71d4c9ef4d8b2183e6ef2fe89756746dce/gymnasium-1.1.1.tar.gz", hash = "sha256:8bd9ea9bdef32c950a444ff36afc785e1d81051ec32d30435058953c20d2456d", size = 829326, upload-time = "2025-03-06T16:30:36.428Z" } wheels = [ @@ -921,7 +925,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.1" +version = "3.10.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -934,20 +938,20 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/08/b89867ecea2e305f408fbb417139a8dd941ecf7b23a2e02157c36da546f0/matplotlib-3.10.1.tar.gz", hash = "sha256:e8d2d0e3881b129268585bf4765ad3ee73a4591d77b9a18c214ac7e3a79fb2ba", size = 36743335, upload-time = "2025-02-27T19:19:51.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/91/d49359a21893183ed2a5b6c76bec40e0b1dcbf8ca148f864d134897cfc75/matplotlib-3.10.3.tar.gz", hash = "sha256:2f82d2c5bb7ae93aaaa4cd42aca65d76ce6376f83304fa3a630b569aca274df0", size = 34799811, upload-time = "2025-05-08T19:10:54.39Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/14/a1b840075be247bb1834b22c1e1d558740b0f618fe3a823740181ca557a1/matplotlib-3.10.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:057206ff2d6ab82ff3e94ebd94463d084760ca682ed5f150817b859372ec4401", size = 8174669, upload-time = "2025-02-27T19:18:34.346Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e4/300b08e3e08f9c98b0d5635f42edabf2f7a1d634e64cb0318a71a44ff720/matplotlib-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a144867dd6bf8ba8cb5fc81a158b645037e11b3e5cf8a50bd5f9917cb863adfe", size = 8047996, upload-time = "2025-02-27T19:18:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/75/f9/8d99ff5a2498a5f1ccf919fb46fb945109623c6108216f10f96428f388bc/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56c5d9fcd9879aa8040f196a235e2dcbdf7dd03ab5b07c0696f80bc6cf04bedd", size = 8461612, upload-time = "2025-02-27T19:18:39.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/b8/53fa08a5eaf78d3a7213fd6da1feec4bae14a81d9805e567013811ff0e85/matplotlib-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f69dc9713e4ad2fb21a1c30e37bd445d496524257dfda40ff4a8efb3604ab5c", size = 8602258, upload-time = "2025-02-27T19:18:43.217Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/4397d2ce808467af86684a622dd112664553e81752ea8bf61bdd89d24a41/matplotlib-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c59af3e8aca75d7744b68e8e78a669e91ccbcf1ac35d0102a7b1b46883f1dd7", size = 9408896, upload-time = "2025-02-27T19:18:45.852Z" }, - { url = "https://files.pythonhosted.org/packages/d7/68/0d03098b3feb786cbd494df0aac15b571effda7f7cbdec267e8a8d398c16/matplotlib-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:11b65088c6f3dae784bc72e8d039a2580186285f87448babb9ddb2ad0082993a", size = 8061281, upload-time = "2025-02-27T19:18:48.919Z" }, - { url = "https://files.pythonhosted.org/packages/7c/1d/5e0dc3b59c034e43de16f94deb68f4ad8a96b3ea00f4b37c160b7474928e/matplotlib-3.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:66e907a06e68cb6cfd652c193311d61a12b54f56809cafbed9736ce5ad92f107", size = 8175488, upload-time = "2025-02-27T19:18:51.436Z" }, - { url = "https://files.pythonhosted.org/packages/7a/81/dae7e14042e74da658c3336ab9799128e09a1ee03964f2d89630b5d12106/matplotlib-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b4bb156abb8fa5e5b2b460196f7db7264fc6d62678c03457979e7d5254b7be", size = 8046264, upload-time = "2025-02-27T19:18:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/21/c4/22516775dcde10fc9c9571d155f90710761b028fc44f660508106c363c97/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1985ad3d97f51307a2cbfc801a930f120def19ba22864182dacef55277102ba6", size = 8452048, upload-time = "2025-02-27T19:18:56.536Z" }, - { url = "https://files.pythonhosted.org/packages/63/23/c0615001f67ce7c96b3051d856baedc0c818a2ed84570b9bf9bde200f85d/matplotlib-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c96f2c2f825d1257e437a1482c5a2cf4fee15db4261bd6fc0750f81ba2b4ba3d", size = 8597111, upload-time = "2025-02-27T19:18:59.439Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c0/a07939a82aed77770514348f4568177d7dadab9787ebc618a616fe3d665e/matplotlib-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35e87384ee9e488d8dd5a2dd7baf471178d38b90618d8ea147aced4ab59c9bea", size = 9402771, upload-time = "2025-02-27T19:19:01.944Z" }, - { url = "https://files.pythonhosted.org/packages/a6/b6/a9405484fb40746fdc6ae4502b16a9d6e53282ba5baaf9ebe2da579f68c4/matplotlib-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfd414bce89cc78a7e1d25202e979b3f1af799e416010a20ab2b5ebb3a02425c", size = 8063742, upload-time = "2025-02-27T19:19:04.632Z" }, + { url = "https://files.pythonhosted.org/packages/f5/bd/af9f655456f60fe1d575f54fb14704ee299b16e999704817a7645dfce6b0/matplotlib-3.10.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0ef061f74cd488586f552d0c336b2f078d43bc00dc473d2c3e7bfee2272f3fa8", size = 8178873, upload-time = "2025-05-08T19:09:53.857Z" }, + { url = "https://files.pythonhosted.org/packages/c2/86/e1c86690610661cd716eda5f9d0b35eaf606ae6c9b6736687cfc8f2d0cd8/matplotlib-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96985d14dc5f4a736bbea4b9de9afaa735f8a0fc2ca75be2fa9e96b2097369d", size = 8052205, upload-time = "2025-05-08T19:09:55.684Z" }, + { url = "https://files.pythonhosted.org/packages/54/51/a9f8e49af3883dacddb2da1af5fca1f7468677f1188936452dd9aaaeb9ed/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5f0283da91e9522bdba4d6583ed9d5521566f63729ffb68334f86d0bb98049", size = 8465823, upload-time = "2025-05-08T19:09:57.442Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e3/c82963a3b86d6e6d5874cbeaa390166458a7f1961bab9feb14d3d1a10f02/matplotlib-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdfa07c0ec58035242bc8b2c8aae37037c9a886370eef6850703d7583e19964b", size = 8606464, upload-time = "2025-05-08T19:09:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/0e/34/24da1027e7fcdd9e82da3194c470143c551852757a4b473a09a012f5b945/matplotlib-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c0b9849a17bce080a16ebcb80a7b714b5677d0ec32161a2cc0a8e5a6030ae220", size = 9413103, upload-time = "2025-05-08T19:10:03.208Z" }, + { url = "https://files.pythonhosted.org/packages/a6/da/948a017c3ea13fd4a97afad5fdebe2f5bbc4d28c0654510ce6fd6b06b7bd/matplotlib-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:eef6ed6c03717083bc6d69c2d7ee8624205c29a8e6ea5a31cd3492ecdbaee1e1", size = 8065492, upload-time = "2025-05-08T19:10:05.271Z" }, + { url = "https://files.pythonhosted.org/packages/eb/43/6b80eb47d1071f234ef0c96ca370c2ca621f91c12045f1401b5c9b28a639/matplotlib-3.10.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ab1affc11d1f495ab9e6362b8174a25afc19c081ba5b0775ef00533a4236eea", size = 8179689, upload-time = "2025-05-08T19:10:07.602Z" }, + { url = "https://files.pythonhosted.org/packages/0f/70/d61a591958325c357204870b5e7b164f93f2a8cca1dc6ce940f563909a13/matplotlib-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2a818d8bdcafa7ed2eed74487fdb071c09c1ae24152d403952adad11fa3c65b4", size = 8050466, upload-time = "2025-05-08T19:10:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/e7/75/70c9d2306203148cc7902a961240c5927dd8728afedf35e6a77e105a2985/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748ebc3470c253e770b17d8b0557f0aa85cf8c63fd52f1a61af5b27ec0b7ffee", size = 8456252, upload-time = "2025-05-08T19:10:11.958Z" }, + { url = "https://files.pythonhosted.org/packages/c4/91/ba0ae1ff4b3f30972ad01cd4a8029e70a0ec3b8ea5be04764b128b66f763/matplotlib-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70453fd99733293ace1aec568255bc51c6361cb0da94fa5ebf0649fdb2150a", size = 8601321, upload-time = "2025-05-08T19:10:14.47Z" }, + { url = "https://files.pythonhosted.org/packages/d2/88/d636041eb54a84b889e11872d91f7cbf036b3b0e194a70fa064eb8b04f7a/matplotlib-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbed9917b44070e55640bd13419de83b4c918e52d97561544814ba463811cbc7", size = 9406972, upload-time = "2025-05-08T19:10:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/b1/79/0d1c165eac44405a86478082e225fce87874f7198300bbebc55faaf6d28d/matplotlib-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf37d8c6ef1a48829443e8ba5227b44236d7fcaf7647caa3178a4ff9f7a5be05", size = 8067954, upload-time = "2025-05-08T19:10:18.663Z" }, ] [[package]] @@ -964,22 +968,22 @@ name = "metadrive-simulator" version = "0.4.2.4" source = { url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl" } dependencies = [ - { name = "filelock", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "gymnasium", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "lxml", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "matplotlib", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "opencv-python-headless", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "panda3d-gltf", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "pillow", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "progressbar", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "psutil", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "pygments", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "requests", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "shapely", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "tqdm", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "yapf", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "filelock" }, + { name = "gymnasium" }, + { name = "lxml" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "opencv-python-headless" }, + { name = "panda3d" }, + { name = "panda3d-gltf" }, + { name = "pillow" }, + { name = "progressbar" }, + { name = "psutil" }, + { name = "pygments" }, + { name = "requests" }, + { name = "shapely" }, + { name = "tqdm" }, + { name = "yapf" }, ] wheels = [ { url = "https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl", hash = "sha256:fbf0ea9be67e65cd45d38ff930e3d49f705dd76c9ddbd1e1482e3f87b61efcef" }, @@ -1238,7 +1242,7 @@ name = "opencv-python-headless" version = "4.11.0.86" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/2f/5b2b3ba52c864848885ba988f24b7f105052f68da9ab0e693cc7c25b0b30/opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798", size = 95177929, upload-time = "2025-01-16T13:53:40.22Z" } wheels = [ @@ -1450,8 +1454,8 @@ name = "panda3d-gltf" version = "0.13" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "panda3d-simplepbr", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d" }, + { name = "panda3d-simplepbr" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/7f/9f18fc3fa843a080acb891af6bcc12262e7bdf1d194a530f7042bebfc81f/panda3d-gltf-0.13.tar.gz", hash = "sha256:d06d373bdd91cf530909b669f43080e599463bbf6d3ef00c3558bad6c6b19675", size = 25573, upload-time = "2021-05-21T05:46:32.738Z" } wheels = [ @@ -1463,8 +1467,8 @@ name = "panda3d-simplepbr" version = "0.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "panda3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "panda3d" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/be/c4d1ded04c22b357277cf6e6a44c1ab4abb285a700bd1991460460e05b99/panda3d_simplepbr-0.13.1.tar.gz", hash = "sha256:c83766d7c8f47499f365a07fe1dff078fc8b3054c2689bdc8dceabddfe7f1a35", size = 6216055, upload-time = "2025-03-30T16:57:41.087Z" } wheels = [ @@ -1528,11 +1532,11 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.3.7" +version = "4.3.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/2d/7d512a3913d60623e7eb945c6d1b4f0bddf1d0b7ada5225274c87e5b53d1/platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351", size = 21291, upload-time = "2025-03-19T20:36:10.989Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", size = 18499, upload-time = "2025-03-19T20:36:09.038Z" }, + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, ] [[package]] @@ -1862,162 +1866,162 @@ name = "pyobjc" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-accessibility", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-accounts", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-addressbook", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-adservices", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-adsupport", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-applescriptkit", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-applescriptobjc", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-apptrackingtransparency", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-audiovideobridging", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-authenticationservices", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-automaticassessmentconfiguration", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-automator", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avfoundation", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avkit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avrouting", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-backgroundassets", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-browserenginekit", marker = "platform_release >= '23.4' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-businesschat", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-calendarstore", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-callkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-carbon", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cfnetwork", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cinematic", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-classkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cloudkit", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-collaboration", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-colorsync", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-contacts", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-contactsui", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreaudiokit", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corebluetooth", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corehaptics", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corelocation", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremediaio", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremidi", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreml", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremotion", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corespotlight", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coretext", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corewlan", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cryptotokenkit", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-datadetection", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-devicecheck", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-devicediscoveryextension", marker = "platform_release >= '24.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-dictionaryservices", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-discrecording", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-discrecordingui", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-diskarbitration", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-dvdplayback", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-eventkit", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-exceptionhandling", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-executionpolicy", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-extensionkit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-externalaccessory", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-fileprovider", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-fileproviderui", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-findersync", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-fsevents", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-gamecenter", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-gamecontroller", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-gamekit", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-gameplaykit", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-healthkit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-imagecapturecore", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-inputmethodkit", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-installerplugins", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-instantmessage", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-intents", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-intentsui", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-iobluetooth", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-iobluetoothui", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-iosurface", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-ituneslibrary", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-kernelmanagement", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-latentsemanticmapping", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-launchservices", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-libdispatch", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-libxpc", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-linkpresentation", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-localauthentication", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-localauthenticationembeddedui", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mailkit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mapkit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mediaaccessibility", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mediaextension", marker = "platform_release >= '24.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-medialibrary", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mediaplayer", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mediatoolbox", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metal", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metalfx", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metalkit", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metalperformanceshaders", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metalperformanceshadersgraph", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metrickit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-mlcompute", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-modelio", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-multipeerconnectivity", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-naturallanguage", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-netfs", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-network", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-networkextension", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-notificationcenter", marker = "platform_release >= '14.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-opendirectory", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-osakit", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-oslog", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-passkit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-pencilkit", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-phase", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-photos", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-photosui", marker = "platform_release >= '15.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-preferencepanes", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-pushkit", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quicklookthumbnailing", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-replaykit", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-safariservices", marker = "platform_release >= '16.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-safetykit", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-scenekit", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-screencapturekit", marker = "platform_release >= '21.4' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-screensaver", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-screentime", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-scriptingbridge", marker = "platform_release >= '9.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-searchkit", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-securityfoundation", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-securityinterface", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-sensitivecontentanalysis", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-servicemanagement", marker = "platform_release >= '10.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-sharedwithyou", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-sharedwithyoucore", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-shazamkit", marker = "platform_release >= '21.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-social", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-soundanalysis", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-speech", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-spritekit", marker = "platform_release >= '13.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-storekit", marker = "platform_release >= '11.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-symbols", marker = "platform_release >= '23.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-syncservices", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-systemconfiguration", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-systemextensions", marker = "platform_release >= '19.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-threadnetwork", marker = "platform_release >= '22.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-uniformtypeidentifiers", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-usernotifications", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-usernotificationsui", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-videosubscriberaccount", marker = "platform_release >= '18.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-videotoolbox", marker = "platform_release >= '12.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-virtualization", marker = "platform_release >= '20.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0' and sys_platform == 'darwin'" }, - { name = "pyobjc-framework-webkit", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-accessibility", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-accounts", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-addressbook" }, + { name = "pyobjc-framework-adservices", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-adsupport", marker = "platform_release >= '18.0'" }, + { name = "pyobjc-framework-applescriptkit" }, + { name = "pyobjc-framework-applescriptobjc", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-applicationservices" }, + { name = "pyobjc-framework-apptrackingtransparency", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-audiovideobridging", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-authenticationservices", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-automaticassessmentconfiguration", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-automator" }, + { name = "pyobjc-framework-avfoundation", marker = "platform_release >= '11.0'" }, + { name = "pyobjc-framework-avkit", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-avrouting", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-backgroundassets", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-browserenginekit", marker = "platform_release >= '23.4'" }, + { name = "pyobjc-framework-businesschat", marker = "platform_release >= '18.0'" }, + { name = "pyobjc-framework-calendarstore", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-callkit", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-carbon" }, + { name = "pyobjc-framework-cfnetwork" }, + { name = "pyobjc-framework-cinematic", marker = "platform_release >= '23.0'" }, + { name = "pyobjc-framework-classkit", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-cloudkit", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-collaboration", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-colorsync", marker = "platform_release >= '17.0'" }, + { name = "pyobjc-framework-contacts", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-contactsui", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-coreaudio" }, + { name = "pyobjc-framework-coreaudiokit" }, + { name = "pyobjc-framework-corebluetooth", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-coredata" }, + { name = "pyobjc-framework-corehaptics", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-corelocation", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-coremedia", marker = "platform_release >= '11.0'" }, + { name = "pyobjc-framework-coremediaio", marker = "platform_release >= '11.0'" }, + { name = "pyobjc-framework-coremidi" }, + { name = "pyobjc-framework-coreml", marker = "platform_release >= '17.0'" }, + { name = "pyobjc-framework-coremotion", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-coreservices" }, + { name = "pyobjc-framework-corespotlight", marker = "platform_release >= '17.0'" }, + { name = "pyobjc-framework-coretext" }, + { name = "pyobjc-framework-corewlan", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-cryptotokenkit", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-datadetection", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-devicecheck", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-devicediscoveryextension", marker = "platform_release >= '24.0'" }, + { name = "pyobjc-framework-dictionaryservices", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-discrecording" }, + { name = "pyobjc-framework-discrecordingui" }, + { name = "pyobjc-framework-diskarbitration" }, + { name = "pyobjc-framework-dvdplayback" }, + { name = "pyobjc-framework-eventkit", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-exceptionhandling" }, + { name = "pyobjc-framework-executionpolicy", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-extensionkit", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-externalaccessory", marker = "platform_release >= '17.0'" }, + { name = "pyobjc-framework-fileprovider", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-fileproviderui", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-findersync", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-fsevents", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-gamecenter", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-gamecontroller", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-gamekit", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-gameplaykit", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-healthkit", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-imagecapturecore", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-inputmethodkit", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-installerplugins" }, + { name = "pyobjc-framework-instantmessage", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-intents", marker = "platform_release >= '16.0'" }, + { name = "pyobjc-framework-intentsui", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-iobluetooth" }, + { name = "pyobjc-framework-iobluetoothui" }, + { name = "pyobjc-framework-iosurface", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-ituneslibrary", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-kernelmanagement", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-latentsemanticmapping" }, + { name = "pyobjc-framework-launchservices" }, + { name = "pyobjc-framework-libdispatch", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-libxpc", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-linkpresentation", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-localauthentication", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-localauthenticationembeddedui", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-mailkit", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-mapkit", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-mediaaccessibility", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-mediaextension", marker = "platform_release >= '24.0'" }, + { name = "pyobjc-framework-medialibrary", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-mediaplayer", marker = "platform_release >= '16.0'" }, + { name = "pyobjc-framework-mediatoolbox", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-metal", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-metalfx", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-metalkit", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-metalperformanceshaders", marker = "platform_release >= '17.0'" }, + { name = "pyobjc-framework-metalperformanceshadersgraph", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-metrickit", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-mlcompute", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-modelio", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-multipeerconnectivity", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-naturallanguage", marker = "platform_release >= '18.0'" }, + { name = "pyobjc-framework-netfs", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-network", marker = "platform_release >= '18.0'" }, + { name = "pyobjc-framework-networkextension", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-notificationcenter", marker = "platform_release >= '14.0'" }, + { name = "pyobjc-framework-opendirectory", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-osakit" }, + { name = "pyobjc-framework-oslog", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-passkit", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-pencilkit", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-phase", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-photos", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-photosui", marker = "platform_release >= '15.0'" }, + { name = "pyobjc-framework-preferencepanes" }, + { name = "pyobjc-framework-pushkit", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-framework-quicklookthumbnailing", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-replaykit", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-safariservices", marker = "platform_release >= '16.0'" }, + { name = "pyobjc-framework-safetykit", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-scenekit", marker = "platform_release >= '11.0'" }, + { name = "pyobjc-framework-screencapturekit", marker = "platform_release >= '21.4'" }, + { name = "pyobjc-framework-screensaver" }, + { name = "pyobjc-framework-screentime", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-scriptingbridge", marker = "platform_release >= '9.0'" }, + { name = "pyobjc-framework-searchkit" }, + { name = "pyobjc-framework-security" }, + { name = "pyobjc-framework-securityfoundation" }, + { name = "pyobjc-framework-securityinterface" }, + { name = "pyobjc-framework-sensitivecontentanalysis", marker = "platform_release >= '23.0'" }, + { name = "pyobjc-framework-servicemanagement", marker = "platform_release >= '10.0'" }, + { name = "pyobjc-framework-sharedwithyou", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-sharedwithyoucore", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-shazamkit", marker = "platform_release >= '21.0'" }, + { name = "pyobjc-framework-social", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-soundanalysis", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-speech", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-spritekit", marker = "platform_release >= '13.0'" }, + { name = "pyobjc-framework-storekit", marker = "platform_release >= '11.0'" }, + { name = "pyobjc-framework-symbols", marker = "platform_release >= '23.0'" }, + { name = "pyobjc-framework-syncservices" }, + { name = "pyobjc-framework-systemconfiguration" }, + { name = "pyobjc-framework-systemextensions", marker = "platform_release >= '19.0'" }, + { name = "pyobjc-framework-threadnetwork", marker = "platform_release >= '22.0'" }, + { name = "pyobjc-framework-uniformtypeidentifiers", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-usernotifications", marker = "platform_release >= '18.0'" }, + { name = "pyobjc-framework-usernotificationsui", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-videosubscriberaccount", marker = "platform_release >= '18.0'" }, + { name = "pyobjc-framework-videotoolbox", marker = "platform_release >= '12.0'" }, + { name = "pyobjc-framework-virtualization", marker = "platform_release >= '20.0'" }, + { name = "pyobjc-framework-vision", marker = "platform_release >= '17.0'" }, + { name = "pyobjc-framework-webkit" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/d6/27b1c9a02f6cb4954984ce1a0239618e52f78c329c7e7450bf1f219b0f0a/pyobjc-11.0.tar.gz", hash = "sha256:a8f7baed65797f67afd46290b02f652c23f4b158ddf960bce0441b78f6803418", size = 11044, upload-time = "2025-01-14T19:02:12.55Z" } wheels = [ @@ -2039,9 +2043,9 @@ name = "pyobjc-framework-accessibility" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/61/7484cc4ad3aa7854cd4c969379a5f044261259d08f7c20b6718493b484f9/pyobjc_framework_accessibility-11.0.tar.gz", hash = "sha256:097450c641fa9ac665199762e77867f2a82775be2f749b8fa69223b828f60656", size = 44597, upload-time = "2025-01-14T19:02:17.596Z" } wheels = [ @@ -2054,8 +2058,8 @@ name = "pyobjc-framework-accounts" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/fa/b64f3f02e0a8b189dc07c391546e2dbe30ef1b3515d1427cdab743545b90/pyobjc_framework_accounts-11.0.tar.gz", hash = "sha256:afc4ae277be1e3e1f90269001c2fd886093a5465e365d7f9a3a0af3e17f06210", size = 17340, upload-time = "2025-01-14T19:02:18.625Z" } wheels = [ @@ -2068,8 +2072,8 @@ name = "pyobjc-framework-addressbook" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/ef/5b5f6b61907ae43509fbf1654e043115d9a64d97efdc28fbb90d06c199f6/pyobjc_framework_addressbook-11.0.tar.gz", hash = "sha256:87073c85bb342eb27faa6eceb7a0e8a4c1e32ad1f2b62bb12dafb5e7b9f15837", size = 97116, upload-time = "2025-01-14T19:02:19.527Z" } wheels = [ @@ -2082,8 +2086,8 @@ name = "pyobjc-framework-adservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/51/7c/0c6e01f83b0c5c7968564a40146f4d07080df278457bdb5a982c8f26a74d/pyobjc_framework_adservices-11.0.tar.gz", hash = "sha256:d2e1a2f395e93e1bbe754ab0d76ce1d64c0d3928472634437e0382eafc6765cd", size = 12732, upload-time = "2025-01-14T19:02:20.559Z" } wheels = [ @@ -2096,8 +2100,8 @@ name = "pyobjc-framework-adsupport" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0c/07/b8b5f741d1e2cad97100444b255e6ecaca3668e7414039981799aa330035/pyobjc_framework_adsupport-11.0.tar.gz", hash = "sha256:20eb8a683d34fb7a6efeceaf964a24b88c3434875c44f66db5e1b609e678043a", size = 12819, upload-time = "2025-01-14T19:02:23.032Z" } wheels = [ @@ -2110,8 +2114,8 @@ name = "pyobjc-framework-applescriptkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/c3/d7f9a33de7ab8e3950350e0862214e66f27ed6bff1a491bc391c377ab83e/pyobjc_framework_applescriptkit-11.0.tar.gz", hash = "sha256:4bafac4a036f0fb8ba01488b8e91d3ac861ce6e61154ffbd0b26f82b99779b50", size = 12638, upload-time = "2025-01-14T19:02:25.1Z" } wheels = [ @@ -2124,8 +2128,8 @@ name = "pyobjc-framework-applescriptobjc" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/9f/bb4fdbcea418f8472d7a67d4d2e4a15fca11fed04648db5208b0fce84807/pyobjc_framework_applescriptobjc-11.0.tar.gz", hash = "sha256:baff9988b6e886aed0e76441358417707de9088be5733f22055fed7904ca1001", size = 12675, upload-time = "2025-01-14T19:02:25.947Z" } wheels = [ @@ -2138,10 +2142,10 @@ name = "pyobjc-framework-applicationservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coretext", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coretext" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ba/fb/4e42573b0d3baa3fa18ec53614cf979f951313f1451e8f2e17df9429da1f/pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2", size = 224334, upload-time = "2025-01-14T19:02:26.828Z" } wheels = [ @@ -2154,8 +2158,8 @@ name = "pyobjc-framework-apptrackingtransparency" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/40/c1c48ed49b5e55c7a635aa1e7ca41ffa1c5547e26243f26489c4768cd730/pyobjc_framework_apptrackingtransparency-11.0.tar.gz", hash = "sha256:cd5c834b5b19c21ad6c317ba5d29f30a8d0ae5d14e7cf557da22abc0850f1e91", size = 13385, upload-time = "2025-01-14T19:02:29.226Z" } wheels = [ @@ -2168,8 +2172,8 @@ name = "pyobjc-framework-audiovideobridging" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/89/5f/0bd5beded0415b53f443da804410eda6a53e1bc64f8779ed9a592719da8c/pyobjc_framework_audiovideobridging-11.0.tar.gz", hash = "sha256:dbc45b06418dd780c365956fdfd69d007436b5ee54c51e671196562eb8290ba6", size = 72418, upload-time = "2025-01-14T19:02:30.083Z" } wheels = [ @@ -2182,8 +2186,8 @@ name = "pyobjc-framework-authenticationservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/0f/2de0d941e9c9b2eb1ce8b22eb31adc7227badfe1e53f615431d3a7fdcd48/pyobjc_framework_authenticationservices-11.0.tar.gz", hash = "sha256:6a060ce651df142e8923d1383449bc6f2c7f5eb0b517152dac609bde3901064e", size = 140036, upload-time = "2025-01-14T19:02:31.115Z" } wheels = [ @@ -2196,8 +2200,8 @@ name = "pyobjc-framework-automaticassessmentconfiguration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/09/d5/5febfee260b88e426c7e799cc95990818feeaa9f740fb9dd516559c96520/pyobjc_framework_automaticassessmentconfiguration-11.0.tar.gz", hash = "sha256:5d3691af2b94e44ca594b6791556e15a9f0a3f9432df51cb891f5f859a65e467", size = 24420, upload-time = "2025-01-14T19:02:32.101Z" } wheels = [ @@ -2210,8 +2214,8 @@ name = "pyobjc-framework-automator" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/1b/1ba4eb296c3915f2e367e45470cb310a9c78b4dd65a37bd522f458f245aa/pyobjc_framework_automator-11.0.tar.gz", hash = "sha256:412d330f8c6f30066cad15e1bdecdc865510bbce469cc7d9477384c4e9f2550f", size = 200905, upload-time = "2025-01-14T19:02:33.039Z" } wheels = [ @@ -2224,11 +2228,11 @@ name = "pyobjc-framework-avfoundation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coreaudio" }, + { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/06/018ad0e2a38dbdbc5c126d7ce37488c4d581d4e2a2b9ef678162bb36d5f6/pyobjc_framework_avfoundation-11.0.tar.gz", hash = "sha256:269a592bdaf8a16948d8935f0cf7c8cb9a53e7ea609a963ada0e55f749ddb530", size = 871064, upload-time = "2025-01-14T19:02:35.757Z" } wheels = [ @@ -2241,9 +2245,9 @@ name = "pyobjc-framework-avkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/79/5b2fcb94b051da32a24b54bb0d90b1d01b190e1402b6303747de47fb17ac/pyobjc_framework_avkit-11.0.tar.gz", hash = "sha256:5fa40919320277b820df3e4c6e84cba91ef7221a28f4eb5374e3dbd80d1e521a", size = 46311, upload-time = "2025-01-14T19:02:37.018Z" } wheels = [ @@ -2256,8 +2260,8 @@ name = "pyobjc-framework-avrouting" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d5/80/63680dc7788bc3573a20fc5421dfcf606970a0cd3b2457829d9b66603ae0/pyobjc_framework_avrouting-11.0.tar.gz", hash = "sha256:54ec9ea0b5adb5149b554e23c07c6b4f4bdb2892ca2ed7b3e88a5de936313025", size = 20561, upload-time = "2025-01-14T19:02:38.157Z" } wheels = [ @@ -2270,8 +2274,8 @@ name = "pyobjc-framework-backgroundassets" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/17/83b873069b0c0763365de88648ad4a2472e9e96fcac39fa534f3633552e8/pyobjc_framework_backgroundassets-11.0.tar.gz", hash = "sha256:9488c3f86bf427898a88b7100e77200c08a487a35c75c1b5735bd69c57ba38cb", size = 23658, upload-time = "2025-01-14T19:02:42.665Z" } wheels = [ @@ -2284,11 +2288,11 @@ name = "pyobjc-framework-browserenginekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coreaudio" }, + { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/2e/df3d2f7e53132d398c2922d331dd1d2aa352997a1a4a1390e59db51c1d13/pyobjc_framework_browserenginekit-11.0.tar.gz", hash = "sha256:51971527f5103c0e09a4ef438c352ebb037fcad8971f8420a781c72ee421f758", size = 31352, upload-time = "2025-01-14T19:02:45.499Z" } wheels = [ @@ -2301,8 +2305,8 @@ name = "pyobjc-framework-businesschat" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/f2/4541989f2c9c5fc3cdfc94ebf31fc6619554b6c22dafdbb57f866a392bc1/pyobjc_framework_businesschat-11.0.tar.gz", hash = "sha256:20fe1c8c848ef3c2e132172d9a007a8aa65b08875a9ca5c27afbfc4396b16dbb", size = 12953, upload-time = "2025-01-14T19:02:46.378Z" } wheels = [ @@ -2315,8 +2319,8 @@ name = "pyobjc-framework-calendarstore" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9f/d3/722c1b16c7d9bdd5c408735c15193e8396f2d22ab6410b0af4569f39c46e/pyobjc_framework_calendarstore-11.0.tar.gz", hash = "sha256:40173f729df56b70ec14f9680962a248c3ce7b4babb46e8b0d760a13975ef174", size = 68475, upload-time = "2025-01-14T19:02:48.544Z" } wheels = [ @@ -2329,8 +2333,8 @@ name = "pyobjc-framework-callkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/0a/9d39ebac92006960b8059f664d8eb7b9cdb8763fe4e8102b2d24b853004f/pyobjc_framework_callkit-11.0.tar.gz", hash = "sha256:52e44a05d0357558e1479977ed2bcb325fabc8d337f641f0249178b5b491fc59", size = 39720, upload-time = "2025-01-14T19:02:50.697Z" } wheels = [ @@ -2343,8 +2347,8 @@ name = "pyobjc-framework-carbon" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/15/51964f36a8ae1002b16d213d2e5ba11cc861bdd9369f1e3f116350d788c5/pyobjc_framework_carbon-11.0.tar.gz", hash = "sha256:476f690f0b34aa9e4cb3923e61481aefdcf33e38ec6087b530a94871eee2b914", size = 37538, upload-time = "2025-01-14T19:02:51.62Z" } wheels = [ @@ -2357,8 +2361,8 @@ name = "pyobjc-framework-cfnetwork" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4f/36/7cebdfb621c7d46eeab3173256bc2e1cba1bbbbe6c0ac8aeb9a4fe2a4627/pyobjc_framework_cfnetwork-11.0.tar.gz", hash = "sha256:eb742fc6a42b248886ff09c3cf247d56e65236864bbea4264e70af8377948d96", size = 78532, upload-time = "2025-01-14T19:02:52.777Z" } wheels = [ @@ -2371,11 +2375,11 @@ name = "pyobjc-framework-cinematic" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-avfoundation" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-framework-metal" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/ef/b5857d567cd6e0366f61c381ebea52383b98d1ac03341f39e779a085812a/pyobjc_framework_cinematic-11.0.tar.gz", hash = "sha256:94a2de8bf3f38bd190311b6bf98d1e2cea7888840b3ce3aa92e464c0216a5cdb", size = 25740, upload-time = "2025-01-14T19:02:54.95Z" } wheels = [ @@ -2388,8 +2392,8 @@ name = "pyobjc-framework-classkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/81/126075eaf5ccf254ddb4cfd99d92a266c30803c5b4572ea3a920fd85e850/pyobjc_framework_classkit-11.0.tar.gz", hash = "sha256:dc5b3856612cafdc7071fbebc252b8908dbf2433e0e5ddb15a0bcd1ee282d27c", size = 39301, upload-time = "2025-01-14T19:02:55.779Z" } wheels = [ @@ -2402,11 +2406,11 @@ name = "pyobjc-framework-cloudkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-accounts", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corelocation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-accounts" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coredata" }, + { name = "pyobjc-framework-corelocation" }, ] sdist = { url = "https://files.pythonhosted.org/packages/89/6c/b0709fed7fc5a1e81de311b9273bb7ba3820a636f8ba880e90510bb6d460/pyobjc_framework_cloudkit-11.0.tar.gz", hash = "sha256:e3f6bf2c3358dd394174b1e69fcec6859951fcd15f6433c6fa3082e3b7e2656d", size = 123034, upload-time = "2025-01-14T19:02:56.769Z" } wheels = [ @@ -2419,7 +2423,7 @@ name = "pyobjc-framework-cocoa" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/32/53809096ad5fc3e7a2c5ddea642590a5f2cb5b81d0ad6ea67fdb2263d9f9/pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5", size = 6173848, upload-time = "2025-01-14T19:03:00.125Z" } wheels = [ @@ -2432,8 +2436,8 @@ name = "pyobjc-framework-collaboration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6b/ee/1f6893eb882af5ecc6a6f4182b2ec85df777c4bc6b9a20a6b42c23abff3f/pyobjc_framework_collaboration-11.0.tar.gz", hash = "sha256:9f53929dd6d5b1a5511494432bf83807041c6f8b9ab6cf6ff184eee0b6f8226f", size = 17084, upload-time = "2025-01-14T19:03:01.98Z" } wheels = [ @@ -2446,8 +2450,8 @@ name = "pyobjc-framework-colorsync" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/24/397a80cd2313cc9e1b73b9acb1de66b740bbece4fe87ed4ea158de8fcef8/pyobjc_framework_colorsync-11.0.tar.gz", hash = "sha256:4f531f6075d9cc4b9d426620a1b04d3aaeb56b5ff178d0a6b0e93d068a5db0d2", size = 39249, upload-time = "2025-01-14T19:03:02.887Z" } wheels = [ @@ -2460,8 +2464,8 @@ name = "pyobjc-framework-contacts" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f5/a2/89053853b28c1f2f2e69092d3e81b7c26073bc8396fc87772b3b1bfb9d57/pyobjc_framework_contacts-11.0.tar.gz", hash = "sha256:fc215baa9f66dbf9ffa1cb8170d102a3546cfd708b2b42de4e9d43645aec03d9", size = 84253, upload-time = "2025-01-14T19:03:03.743Z" } wheels = [ @@ -2474,9 +2478,9 @@ name = "pyobjc-framework-contactsui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-contacts", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-contacts" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3f/67/122b16fd7f2da7f0f48c1d7fcaf0f1951253ddd5489d909a1b5fb80f3925/pyobjc_framework_contactsui-11.0.tar.gz", hash = "sha256:d0f2a4afea807fbe4db1518c4f81f0dc9aa1817fe7cb16115308fc00375a70db", size = 19486, upload-time = "2025-01-14T19:03:04.72Z" } wheels = [ @@ -2489,8 +2493,8 @@ name = "pyobjc-framework-coreaudio" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/e6/3b7a8af3defec012d6cacf277fd8d5c3e254ceace63a05447dc1119f3a7e/pyobjc_framework_coreaudio-11.0.tar.gz", hash = "sha256:38b6b531381119be6998cf704d04c9ea475aaa33f6dd460e0584351475acd0ae", size = 140507, upload-time = "2025-01-14T19:03:05.612Z" } wheels = [ @@ -2503,9 +2507,9 @@ name = "pyobjc-framework-coreaudiokit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreaudio", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coreaudio" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/1a/604cac8d992b6e66adbb98edb1f65820116f5d74d8decd6d43898ae2929d/pyobjc_framework_coreaudiokit-11.0.tar.gz", hash = "sha256:1a4c3de4a02b0dfa7410c012c7f0939edd2e127d439fb934aeafc68450615f1d", size = 21450, upload-time = "2025-01-14T19:03:06.681Z" } wheels = [ @@ -2518,8 +2522,8 @@ name = "pyobjc-framework-corebluetooth" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/74/66a62a36da9db5924ee15de6fe1eb544930609b307b3bfbc021b5cf43781/pyobjc_framework_corebluetooth-11.0.tar.gz", hash = "sha256:1dcb7c039c2efa7c72dc14cdda80e677240b49fa38999941a77ee02ca142998d", size = 59797, upload-time = "2025-01-14T19:03:07.584Z" } wheels = [ @@ -2532,8 +2536,8 @@ name = "pyobjc-framework-coredata" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/84/22/6787205b91cb6d526b6b472ebaa5baff275200774050a55b4b25d2bd957a/pyobjc_framework_coredata-11.0.tar.gz", hash = "sha256:b11acb51ff31cfb69a53f4e127996bf194bcac770e8fa67cb5ba3fb16a496058", size = 260029, upload-time = "2025-01-14T19:03:08.609Z" } wheels = [ @@ -2546,8 +2550,8 @@ name = "pyobjc-framework-corehaptics" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2a/b8/66481497362171e7ad42fc8fcc0272c04b95a707c5c1e7e8f8a8bfe58917/pyobjc_framework_corehaptics-11.0.tar.gz", hash = "sha256:1949b56ac0bd4219eb04c466cdd0f7f93d6826ed92ee61f01a4b5e98139ee039", size = 42956, upload-time = "2025-01-14T19:03:09.753Z" } wheels = [ @@ -2560,8 +2564,8 @@ name = "pyobjc-framework-corelocation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0a/2d/b21ca49a34db49390420a9d7d05fd9eb89850dbec0a555c9ee408f52609c/pyobjc_framework_corelocation-11.0.tar.gz", hash = "sha256:05055c3b567f7f8f796845da43fb755d84d630909b927a39f25cf706ef52687d", size = 103955, upload-time = "2025-01-14T19:03:10.707Z" } wheels = [ @@ -2574,8 +2578,8 @@ name = "pyobjc-framework-coremedia" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/60/7c7b9f13c94910882de6cc08f48a52cce9739e75cc3b3b6de5c857e6536a/pyobjc_framework_coremedia-11.0.tar.gz", hash = "sha256:a414db97ba30b43c9dd96213459d6efb169f9e92ce1ad7a75516a679b181ddfb", size = 249161, upload-time = "2025-01-14T19:03:12.291Z" } wheels = [ @@ -2588,8 +2592,8 @@ name = "pyobjc-framework-coremediaio" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/59/904af57d302caa4c20d3bfebb9fb9300ccc3c396134460821c9f1e8ab65b/pyobjc_framework_coremediaio-11.0.tar.gz", hash = "sha256:7d652cf1a2a75c78ea6e8dbc7fc8b782bfc0f07eafc84b700598172c82f373d8", size = 107856, upload-time = "2025-01-14T19:03:14.225Z" } wheels = [ @@ -2602,8 +2606,8 @@ name = "pyobjc-framework-coremidi" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/90/d004cdf4c52b8b16842e15135495de882d743b4f0217946bd8ae1a920173/pyobjc_framework_coremidi-11.0.tar.gz", hash = "sha256:acace4448b3e4802ab5dd75bbf875aae5e1f6c8cab2b2f1d58af20fc8b2a5a7f", size = 107342, upload-time = "2025-01-14T19:03:15.235Z" } wheels = [ @@ -2616,8 +2620,8 @@ name = "pyobjc-framework-coreml" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2e/64/4f0a990ec0955fe9b88f1fa58303c8471c551996670216527b4ac559ed8f/pyobjc_framework_coreml-11.0.tar.gz", hash = "sha256:143a1f73a0ea0a0ea103f3175cb87a61bbcb98f70f85320ed4c61302b9156d58", size = 81452, upload-time = "2025-01-14T19:03:16.283Z" } wheels = [ @@ -2630,8 +2634,8 @@ name = "pyobjc-framework-coremotion" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/be/79/5c4ff39a48f0dc0f764d1330b2360e9f31e3a32414e8690e7f20e4574e93/pyobjc_framework_coremotion-11.0.tar.gz", hash = "sha256:d1e7ca418897e35365d07c6fd5b5d625a3c44261b6ce46dcf80787f634ad6fa5", size = 66508, upload-time = "2025-01-14T19:03:17.254Z" } wheels = [ @@ -2644,9 +2648,9 @@ name = "pyobjc-framework-coreservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-fsevents", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-fsevents" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/b5/19c096b9938d6e2fdb1b436f21ad989b77dbeb4e59b3db4bd344800fa1e8/pyobjc_framework_coreservices-11.0.tar.gz", hash = "sha256:ac96954f1945a1153bdfef685611665749eaa8016b5af6f34bd56a274952b03a", size = 1244406, upload-time = "2025-01-14T19:03:19.202Z" } wheels = [ @@ -2659,8 +2663,8 @@ name = "pyobjc-framework-corespotlight" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/6a/6707d7ef339b9ad2dd0994d1df42969ee3b231f2d098f3377d40aed60b4f/pyobjc_framework_corespotlight-11.0.tar.gz", hash = "sha256:a96c9b4ba473bc3ee19afa01a9af989458e6a56e9656c2cdea1850d2b13720e6", size = 86130, upload-time = "2025-01-14T19:03:20.457Z" } wheels = [ @@ -2673,9 +2677,9 @@ name = "pyobjc-framework-coretext" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/e8/9b68dc788828e38143a3e834e66346713751cb83d7f0955016323005c1a2/pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43", size = 274222, upload-time = "2025-01-14T19:03:21.521Z" } wheels = [ @@ -2688,8 +2692,8 @@ name = "pyobjc-framework-corewlan" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2e/a9/cda522b270adb75d62bae447b2131da62912b5eda058a07e3a433689116f/pyobjc_framework_corewlan-11.0.tar.gz", hash = "sha256:8803981d64e3eb4fa0ea56657a9b98e4004de5a84d56e32e5444815d8ed6fa6f", size = 65254, upload-time = "2025-01-14T19:03:23.938Z" } wheels = [ @@ -2702,8 +2706,8 @@ name = "pyobjc-framework-cryptotokenkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b8/72/b871fa5476479e4a22a4a0e971fb4724b0eb94c721365539ad55f4dc3135/pyobjc_framework_cryptotokenkit-11.0.tar.gz", hash = "sha256:a1bbfe9170c35cb427d39167af55aefea651c5c8a45c0de60226dae04b61a6b1", size = 58734, upload-time = "2025-01-14T19:03:24.851Z" } wheels = [ @@ -2716,8 +2720,8 @@ name = "pyobjc-framework-datadetection" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/6b/b896feb16e914dc81b6ed6cdbd0b6e6390eaafc80fff5297ec17eb0bd716/pyobjc_framework_datadetection-11.0.tar.gz", hash = "sha256:9967555151892f8400cffac86e8656f2cb8d7866963fdee255e0747fa1386533", size = 13738, upload-time = "2025-01-14T19:03:27.054Z" } wheels = [ @@ -2730,8 +2734,8 @@ name = "pyobjc-framework-devicecheck" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/f8/237a92dd9ba8a88b7027f78cba83e61b0011bfc2a49351ecaa177233f639/pyobjc_framework_devicecheck-11.0.tar.gz", hash = "sha256:66cff0323dc8eef1b76d60f9c9752684f11e534ebda60ecbf6858a9c73553f64", size = 14198, upload-time = "2025-01-14T19:03:27.918Z" } wheels = [ @@ -2744,8 +2748,8 @@ name = "pyobjc-framework-devicediscoveryextension" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/48/178a1879109128f34334fdae2fe4463c7620f169593bea96704f347d945e/pyobjc_framework_devicediscoveryextension-11.0.tar.gz", hash = "sha256:576dac3f418cfc4f71020a45f06231d14e4b2a8e182ef0020dd9da3cf238d02f", size = 14511, upload-time = "2025-01-14T19:03:32.132Z" } wheels = [ @@ -2758,8 +2762,8 @@ name = "pyobjc-framework-dictionaryservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-coreservices" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/cf/2913c7df737eb8519acb7ef6429127e40d6c334415e38cfa18d6481150eb/pyobjc_framework_dictionaryservices-11.0.tar.gz", hash = "sha256:6b5f27c75424860f169e7c7e182fabffdba22854fedb8023de180e8770661dce", size = 10823, upload-time = "2025-01-14T19:03:32.942Z" } wheels = [ @@ -2772,8 +2776,8 @@ name = "pyobjc-framework-discrecording" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/cc/f36612b67ca1fff7659d7933b563dce61f8c84dad0bf79fab08bb34949ad/pyobjc_framework_discrecording-11.0.tar.gz", hash = "sha256:6bdc533f067d049ea5032f65af70b5cdab68673574ac32dacb46509a9411d256", size = 122426, upload-time = "2025-01-14T19:03:35.589Z" } wheels = [ @@ -2786,9 +2790,9 @@ name = "pyobjc-framework-discrecordingui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-discrecording", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-discrecording" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d4/6b/3c120c59a939854dd4b7a162fad47011375c5ba00a12940f7217aea90eeb/pyobjc_framework_discrecordingui-11.0.tar.gz", hash = "sha256:bec8a252fd2022dce6c58b1f3366a7295efb0c7c77817f11f9efcce70527d7a2", size = 19614, upload-time = "2025-01-14T19:03:36.695Z" } wheels = [ @@ -2801,8 +2805,8 @@ name = "pyobjc-framework-diskarbitration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/fb/5d3ff093144f499904b1e1bce18d010fe2171b9be62b4679d3dda8b3ad19/pyobjc_framework_diskarbitration-11.0.tar.gz", hash = "sha256:1c3e21398b366a1ce96cf68501a2e415f5ccad4b43a3e7cc901e09e896dfb545", size = 20096, upload-time = "2025-01-14T19:03:37.659Z" } wheels = [ @@ -2815,8 +2819,8 @@ name = "pyobjc-framework-dvdplayback" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/89/89ebee4863fd6f173bff9373b5bda4ffa87eba6197337617ab086e23c7d5/pyobjc_framework_dvdplayback-11.0.tar.gz", hash = "sha256:9a005f441afbc34aea301857e166fd650d82762a75d024253e18d1102b21b2f8", size = 64798, upload-time = "2025-01-14T19:03:38.491Z" } wheels = [ @@ -2829,8 +2833,8 @@ name = "pyobjc-framework-eventkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/13/38a98e5cee62e1655d84cfb88cad54fdec4ec272b5fd0c5ac3fc21e33e49/pyobjc_framework_eventkit-11.0.tar.gz", hash = "sha256:3d412203a510b3d62a5eb0987406e0951b13ed39c3351c0ec874afd72496627c", size = 75399, upload-time = "2025-01-14T19:03:39.441Z" } wheels = [ @@ -2843,8 +2847,8 @@ name = "pyobjc-framework-exceptionhandling" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/46/6c2c4805697a0cfb8413eb7bc6901298e7a1febd49bb1ea960274fc33af3/pyobjc_framework_exceptionhandling-11.0.tar.gz", hash = "sha256:b11562c6eeaef5d8d43e9d817cf50feceb02396e5eb6a7f61df2c0cec93d912b", size = 18157, upload-time = "2025-01-14T19:03:40.393Z" } wheels = [ @@ -2857,8 +2861,8 @@ name = "pyobjc-framework-executionpolicy" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/91/2e4cacbdabf01bc1207817edacc814b6bc486df12e857a8d86964d98fef4/pyobjc_framework_executionpolicy-11.0.tar.gz", hash = "sha256:de953a8acae98079015b19e75ec8154a311ac1a70fb6d885e17fab09464c98a9", size = 13753, upload-time = "2025-01-14T19:03:42.353Z" } wheels = [ @@ -2871,8 +2875,8 @@ name = "pyobjc-framework-extensionkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/98/803e3cb000dac227eb0d223802a0aeb052d34a741e572d9584e7d83afca7/pyobjc_framework_extensionkit-11.0.tar.gz", hash = "sha256:82d9e79532e5a0ff0eadf1ccac236c5d3dca344e1090a0f3e88519faa24143c7", size = 19200, upload-time = "2025-01-14T19:03:43.188Z" } wheels = [ @@ -2885,8 +2889,8 @@ name = "pyobjc-framework-externalaccessory" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/67/b0/ac0a02fe26e66c33fee751a65c1ed06bbd2934db8636e08bb491e8334bad/pyobjc_framework_externalaccessory-11.0.tar.gz", hash = "sha256:39e59331ced75cdcccf23bb5ffe0fa9d67e0c190c1da8887a0e4349b7e27584f", size = 22577, upload-time = "2025-01-14T19:03:44.021Z" } wheels = [ @@ -2899,8 +2903,8 @@ name = "pyobjc-framework-fileprovider" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/fc/b8593d8645b9933e60a885f451d0c12d9c0e1b00e62121d8660d95852dff/pyobjc_framework_fileprovider-11.0.tar.gz", hash = "sha256:dcc3ac3c90117c1b8027ea5f26dad6fe5045f688ce3e60d07ece12ec56e17ab3", size = 78701, upload-time = "2025-01-14T19:03:44.931Z" } wheels = [ @@ -2913,8 +2917,8 @@ name = "pyobjc-framework-fileproviderui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-fileprovider", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-fileprovider" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/9d/ca4aed36e6188623e9da633634af772f239bee74934322e1c19ae7b79a53/pyobjc_framework_fileproviderui-11.0.tar.gz", hash = "sha256:cf5c7d32b29d344b65217397eea7b1a2913ce52ce923c9e04135a7a298848d04", size = 13419, upload-time = "2025-01-14T19:03:46.016Z" } wheels = [ @@ -2927,8 +2931,8 @@ name = "pyobjc-framework-findersync" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/e3/24df6e24b589073815be13f2943b93feb12afbf558f6e54c4033b57c29ee/pyobjc_framework_findersync-11.0.tar.gz", hash = "sha256:8dab3feff5debd6bc3746a21ded991716723d98713d1ba37cec1c5e2ad78ee63", size = 15295, upload-time = "2025-01-14T19:03:46.91Z" } wheels = [ @@ -2941,8 +2945,8 @@ name = "pyobjc-framework-fsevents" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/37/4c09cc7b8678e2bb5b68ebc62e817eb88c409b1c41bdc1510d7d24a0372d/pyobjc_framework_fsevents-11.0.tar.gz", hash = "sha256:e01dab04704a518e4c3e1f7d8722819a4f228d5082978e11618aa7abba3883fe", size = 29078, upload-time = "2025-01-14T19:03:49.762Z" } wheels = [ @@ -2955,8 +2959,8 @@ name = "pyobjc-framework-gamecenter" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7f/3b/e66caebc948d9fe3b2671659caab220aff6d5e80ac25442d83331b523d23/pyobjc_framework_gamecenter-11.0.tar.gz", hash = "sha256:18a05500dbcf2cca4a0f05839ec010c76ee08ab65b65020c9538a31feb274483", size = 31459, upload-time = "2025-01-14T19:03:50.766Z" } wheels = [ @@ -2969,8 +2973,8 @@ name = "pyobjc-framework-gamecontroller" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fa/30/02ca5a4fb911acf3e8018abcbd29631a842aeac02958ae91fab1acb13ad1/pyobjc_framework_gamecontroller-11.0.tar.gz", hash = "sha256:6d62f4493d634eba03a43a14c4d1e4511e1e3a2ca2e9cbefa6ae9278a272c1d0", size = 115318, upload-time = "2025-01-14T19:03:52.264Z" } wheels = [ @@ -2983,9 +2987,9 @@ name = "pyobjc-framework-gamekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3f/df/c161460e5736a34f9b59aa0a3f2d6ad1d1cd9a913aa63c89c41a6ba3b6ae/pyobjc_framework_gamekit-11.0.tar.gz", hash = "sha256:29b5464ca78f0de62e6b6d56e80bbeccb96dc13820b6d5b4e835ab1cc127e5b9", size = 164394, upload-time = "2025-01-14T19:03:53.762Z" } wheels = [ @@ -2998,9 +3002,9 @@ name = "pyobjc-framework-gameplaykit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-spritekit", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-spritekit" }, ] sdist = { url = "https://files.pythonhosted.org/packages/41/f0/980c4fc3c594d9726b7eb6ae83f73127b22560e1541c7d272d23d17fdf0d/pyobjc_framework_gameplaykit-11.0.tar.gz", hash = "sha256:90eeec464fba992d75a406ccbddb35ed7420a4f5226f19c018982fa3ba7bf431", size = 72837, upload-time = "2025-01-14T19:03:56.127Z" } wheels = [ @@ -3013,8 +3017,8 @@ name = "pyobjc-framework-healthkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7b/2f/d79d2ec7c23bfc94bfaa7b7c6f6487a8bffdb73263eea6900aab56135889/pyobjc_framework_healthkit-11.0.tar.gz", hash = "sha256:e78ccb05f747ae3e70b5d73522030b7ba01ef2d390155fba7d50c1c614ae241f", size = 201558, upload-time = "2025-01-14T19:03:57.117Z" } wheels = [ @@ -3027,8 +3031,8 @@ name = "pyobjc-framework-imagecapturecore" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/fe/db1fc3ffd784a9010070cd87a05d7fd2542c400395589341fab5970a01e1/pyobjc_framework_imagecapturecore-11.0.tar.gz", hash = "sha256:f5d185d8c8b564f8b4a815381bcdb424b10d203ba5bdf0fc887085e007df6f7a", size = 99935, upload-time = "2025-01-14T19:03:58.548Z" } wheels = [ @@ -3041,8 +3045,8 @@ name = "pyobjc-framework-inputmethodkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/13d007285582e598903264a7d25cc6771a2a52d6c2a96a68fe91db0844fb/pyobjc_framework_inputmethodkit-11.0.tar.gz", hash = "sha256:86cd648bf98c4e777c884b7f69ebcafba84866740430d297645bf388eee6ce52", size = 26684, upload-time = "2025-01-14T19:03:59.525Z" } wheels = [ @@ -3055,8 +3059,8 @@ name = "pyobjc-framework-installerplugins" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f2/f3/0379655e8ea3566002768d5e7b3ccd72ca845390632a8dabf801348af3a7/pyobjc_framework_installerplugins-11.0.tar.gz", hash = "sha256:88ec84e6999e8b2df874758b09878504a4fbfc8471cf3cd589d57e556f5b916e", size = 27687, upload-time = "2025-01-14T19:04:00.515Z" } wheels = [ @@ -3069,9 +3073,9 @@ name = "pyobjc-framework-instantmessage" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/4d/6810a1f2039ff24d9498858b3ebb46357d4091aa5cec9ff4e41bbcdb25de/pyobjc_framework_instantmessage-11.0.tar.gz", hash = "sha256:ec5c4c70c9b0e61ae82888067246e4f931e700d625b3c42604e54759d4fbf65c", size = 34027, upload-time = "2025-01-14T19:04:01.405Z" } wheels = [ @@ -3084,8 +3088,8 @@ name = "pyobjc-framework-intents" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/88/07e47b0c5c46fe97c23c883ae7a053c2ca6f6fd6afe851d1c2c784644f0f/pyobjc_framework_intents-11.0.tar.gz", hash = "sha256:6405c816dfed8ffa8b3f8b0fae75f61d64787dbae8db1c475bb4450cf8fdf6b5", size = 447921, upload-time = "2025-01-14T19:04:02.487Z" } wheels = [ @@ -3098,8 +3102,8 @@ name = "pyobjc-framework-intentsui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-intents", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-intents" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/96/3b3b367f70a4d0a60d2c6251e4a1f4bf470945ae939e0ba20e6d56d10c7a/pyobjc_framework_intentsui-11.0.tar.gz", hash = "sha256:4ce04f926c823fbc1fba7d9c5b33d512b514396719e6bc50ef65b82774e42bc5", size = 20774, upload-time = "2025-01-14T19:04:03.648Z" } wheels = [ @@ -3112,8 +3116,8 @@ name = "pyobjc-framework-iobluetooth" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1e/46/62913f8e5ac307b154b3dd50a7a0b167c9d7ac2a579223e33208c141c387/pyobjc_framework_iobluetooth-11.0.tar.gz", hash = "sha256:869f01f573482da92674abbae4a154143e993b1fe4b2c3523f9e0f9c48b798d4", size = 300463, upload-time = "2025-01-14T19:04:04.582Z" } wheels = [ @@ -3126,8 +3130,8 @@ name = "pyobjc-framework-iobluetoothui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-iobluetooth", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-iobluetooth" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/55/d194de8cfa63c96970e6c90c35e80ce3fceb42934a85d3728736a0e416ff/pyobjc_framework_iobluetoothui-11.0.tar.gz", hash = "sha256:a583758d3e54149ee2dcf00374685aa99e8ae407e044f7c378acc002f9f27e63", size = 23091, upload-time = "2025-01-14T19:04:05.659Z" } wheels = [ @@ -3140,8 +3144,8 @@ name = "pyobjc-framework-iosurface" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/91/ae9ca9e1a777eb786d9d43649437d01d24386736cffe9bb2f504b57e8db6/pyobjc_framework_iosurface-11.0.tar.gz", hash = "sha256:24da8d1cf9356717b1c7e75a1c61e9a9417b62f051d13423a4a7b0978d3dcda5", size = 20555, upload-time = "2025-01-14T19:04:09.475Z" } wheels = [ @@ -3154,8 +3158,8 @@ name = "pyobjc-framework-ituneslibrary" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/41/fe/881ab1058d795fe68ccc1e14df0d5e161601dced15d3be84105ecc44bae6/pyobjc_framework_ituneslibrary-11.0.tar.gz", hash = "sha256:2e15dcfbb9d5e95634ddff153de159a28f5879f1a13fdf95504e011773056c6e", size = 47647, upload-time = "2025-01-14T19:04:11.333Z" } wheels = [ @@ -3168,8 +3172,8 @@ name = "pyobjc-framework-kernelmanagement" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4a/ea/8ef534fce78817fc577f18de2b34e363873f785894f2bbbfc694823f5088/pyobjc_framework_kernelmanagement-11.0.tar.gz", hash = "sha256:812479d5f85eae27aeeaa22f64c20b926b28b5b9b2bf31c8eab9496d3e038028", size = 12794, upload-time = "2025-01-14T19:04:14.204Z" } wheels = [ @@ -3182,8 +3186,8 @@ name = "pyobjc-framework-latentsemanticmapping" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/29/8838eefeb82da95931134b06624364812dedf7e9cc905f36d95d497f2904/pyobjc_framework_latentsemanticmapping-11.0.tar.gz", hash = "sha256:6f578c3e0a171706bdbfcfc2c572a8059bf8039d22c1475df13583749a35cec1", size = 17704, upload-time = "2025-01-14T19:04:14.972Z" } wheels = [ @@ -3196,8 +3200,8 @@ name = "pyobjc-framework-launchservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-coreservices" }, ] sdist = { url = "https://files.pythonhosted.org/packages/da/59/eb847389224c670c885ae3d008b1ffe3b996bbe094b43e49dfa84f3947a9/pyobjc_framework_launchservices-11.0.tar.gz", hash = "sha256:7c5c8a8cec013e2cb3fa82a167ca2d61505c36a79f75c718f3f913e597f9ffee", size = 20691, upload-time = "2025-01-14T19:04:15.884Z" } wheels = [ @@ -3210,8 +3214,8 @@ name = "pyobjc-framework-libdispatch" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ab/33/4ec96a9edd37948f09e94635852c2db695141430cc1adc7b25968e1f3a95/pyobjc_framework_libdispatch-11.0.tar.gz", hash = "sha256:d22df11b07b1c3c8e7cfc4ba9e876a95c19f44acd36cf13d40c5cccc1ffda04b", size = 53496, upload-time = "2025-01-14T19:04:16.82Z" } wheels = [ @@ -3224,8 +3228,8 @@ name = "pyobjc-framework-libxpc" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/7e/9fa73ce6925db9cfd8a6b45d97943af8fe59f92251e7fd201b6e4608c172/pyobjc_framework_libxpc-11.0.tar.gz", hash = "sha256:e0c336913ab6a526b036915aa9038de2a5281e696ac2d3db3347b3040519c11d", size = 48627, upload-time = "2025-01-14T19:04:17.728Z" } wheels = [ @@ -3238,9 +3242,9 @@ name = "pyobjc-framework-linkpresentation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/95/5c/dac9fe4ad0a4076c863b5ac9925e751fc18c637ae411e4891c4b7558a5b3/pyobjc_framework_linkpresentation-11.0.tar.gz", hash = "sha256:bc4ace4aab4da4a4e4df10517bd478b6d51ebf00b423268ee8d9f356f9e87be9", size = 15231, upload-time = "2025-01-14T19:04:20.763Z" } wheels = [ @@ -3253,9 +3257,9 @@ name = "pyobjc-framework-localauthentication" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-security" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ec/b1/bea4b5f8adbb69c0b34eddee63e052f35271cc630db43fbef6873352e21f/pyobjc_framework_localauthentication-11.0.tar.gz", hash = "sha256:eb55a3de647894092d6ed3f8f13fdc38e5dbf4850be320ea14dd2ac83176b298", size = 40020, upload-time = "2025-01-14T19:04:22.206Z" } wheels = [ @@ -3268,9 +3272,9 @@ name = "pyobjc-framework-localauthenticationembeddedui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-localauthentication", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-localauthentication" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/ee/821f2d2e9da4cba3dc47e50c8367c6405e91551fb7d8ec842858d5b1d45d/pyobjc_framework_localauthenticationembeddedui-11.0.tar.gz", hash = "sha256:7e9bf6df77ff12a4e827988d8578c15b4431694b2fcfd5b0dad5d7738757ee6a", size = 14204, upload-time = "2025-01-14T19:04:23.566Z" } wheels = [ @@ -3283,8 +3287,8 @@ name = "pyobjc-framework-mailkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d8/79/9c9140f726ba14898762ddc19e7142724e0ce5930f08eb20f33f78b05be8/pyobjc_framework_mailkit-11.0.tar.gz", hash = "sha256:d08a2dcc95b5e7955c7c385fe6e018325113d02c007c4178d3fb3c9ab326c163", size = 32274, upload-time = "2025-01-14T19:04:25.086Z" } wheels = [ @@ -3297,10 +3301,10 @@ name = "pyobjc-framework-mapkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-corelocation", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-corelocation" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/7e/ef86c6e218a58bb9497ce9754a77f12ffe01c4b3609279727b7d7e44655a/pyobjc_framework_mapkit-11.0.tar.gz", hash = "sha256:cd8a91df4c0b442fcf1b14d735e566a06b21b3f48a2a4afe269fca45bfa49117", size = 165080, upload-time = "2025-01-14T19:04:26.606Z" } wheels = [ @@ -3313,8 +3317,8 @@ name = "pyobjc-framework-mediaaccessibility" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/81/8e/9fe2cb251ff6107a03bafa07f63b6593df145a2579fffb096023fb21b167/pyobjc_framework_mediaaccessibility-11.0.tar.gz", hash = "sha256:1298cc0128e1c0724e8f8e63a6167ea6809a985922c67399b997f8243de59ab4", size = 18671, upload-time = "2025-01-14T19:04:27.624Z" } wheels = [ @@ -3327,10 +3331,10 @@ name = "pyobjc-framework-mediaextension" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-avfoundation" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coremedia" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/1f/e31d9431bc71077b09583ea863b3c91b7de9371d0cc17a8be99be8119daa/pyobjc_framework_mediaextension-11.0.tar.gz", hash = "sha256:ecd8a64939e1c16be005690117c21fd406fc04d3036e2adea7600d2a0c53f4ea", size = 57931, upload-time = "2025-01-14T19:04:28.65Z" } wheels = [ @@ -3343,9 +3347,9 @@ name = "pyobjc-framework-medialibrary" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a8/a4/8c7d1635994800dc412a5db2c4b43ed499184651efcec0c8da3cf8e2bcc7/pyobjc_framework_medialibrary-11.0.tar.gz", hash = "sha256:692889fab1e479a9c207f0ff23c900dad5f47caf47c05cc995d9bb7c1e56e8b9", size = 18975, upload-time = "2025-01-14T19:04:29.739Z" } wheels = [ @@ -3358,8 +3362,8 @@ name = "pyobjc-framework-mediaplayer" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-avfoundation" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/3d2783f2f96ddf51bebcf6537a4a0f2a8a1fe4e520de218fc1b7c5b219ed/pyobjc_framework_mediaplayer-11.0.tar.gz", hash = "sha256:c61be0ba6c648db6b1d013a52f9afb8901a8d7fbabd983df2175c1b1fbff81e5", size = 94020, upload-time = "2025-01-14T19:04:30.617Z" } wheels = [ @@ -3372,8 +3376,8 @@ name = "pyobjc-framework-mediatoolbox" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/da/46/cf5f3bde6cad32f10095850ca44f24ba241d18b26379187c412be1260f39/pyobjc_framework_mediatoolbox-11.0.tar.gz", hash = "sha256:de949a44f10b5a15e5a7131ee53b2806b8cb753fd01a955970ec0f475952ba24", size = 23067, upload-time = "2025-01-14T19:04:32.823Z" } wheels = [ @@ -3386,8 +3390,8 @@ name = "pyobjc-framework-metal" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/e0/a6d18a1183410a5d8610ca1ae6c065b8944586441f8669faee7509817246/pyobjc_framework_metal-11.0.tar.gz", hash = "sha256:cad390150aa63502d5cfe242026b55ed39ffaf816342ddf51e44a9aead6c24be", size = 446102, upload-time = "2025-01-14T19:04:34.011Z" } wheels = [ @@ -3400,8 +3404,8 @@ name = "pyobjc-framework-metalfx" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-metal" }, ] sdist = { url = "https://files.pythonhosted.org/packages/68/cf/ff9367e4737a12ebd12a17e693ec247028cf065761acc073ebefb2b2393a/pyobjc_framework_metalfx-11.0.tar.gz", hash = "sha256:2ae41991bf7a733c44fcd5b6550cedea3accaaf0f529643975d3da113c9f0caa", size = 26436, upload-time = "2025-01-14T19:04:36.161Z" } wheels = [ @@ -3414,9 +3418,9 @@ name = "pyobjc-framework-metalkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-metal" }, ] sdist = { url = "https://files.pythonhosted.org/packages/92/27/fb3c1b10914abf2ae6682837abf76bcd8cb7af2ba613fbc55fb9d055bb95/pyobjc_framework_metalkit-11.0.tar.gz", hash = "sha256:1bbbe35c7c6a481383d32f6eaae59a1cd8084319a65c1aa343d63a257d8b4ddb", size = 44628, upload-time = "2025-01-14T19:04:36.977Z" } wheels = [ @@ -3429,8 +3433,8 @@ name = "pyobjc-framework-metalperformanceshaders" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metal", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-metal" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/c2/c08996a8c6cfef09fb9e726cc99b0bf3ad0ffcef66d5c2543e6b35dd4e2e/pyobjc_framework_metalperformanceshaders-11.0.tar.gz", hash = "sha256:41179e3a11e55325153fffd84f48946d47c1dc1944677febd871a127021e056d", size = 301444, upload-time = "2025-01-14T19:04:38.064Z" } wheels = [ @@ -3443,8 +3447,8 @@ name = "pyobjc-framework-metalperformanceshadersgraph" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-metalperformanceshaders", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-metalperformanceshaders" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/b8/353852c76eb437e907ca0acf8a5b5f9255e9b9ee8c0706b69b0c17498f97/pyobjc_framework_metalperformanceshadersgraph-11.0.tar.gz", hash = "sha256:33077ebbbe1aa7787de2552a83534be6c439d7f4272de17915a85fda8fd3b72d", size = 105381, upload-time = "2025-01-14T19:04:39.831Z" } wheels = [ @@ -3457,8 +3461,8 @@ name = "pyobjc-framework-metrickit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/82/605ad654f40ff4480ba9366ad3726da80c98e33b73f122fb91259be1ce81/pyobjc_framework_metrickit-11.0.tar.gz", hash = "sha256:ee3da403863beec181a2d6dc7b7eeb4d07e954b88bbabac58a82523b2f83fdc7", size = 40414, upload-time = "2025-01-14T19:04:41.186Z" } wheels = [ @@ -3471,8 +3475,8 @@ name = "pyobjc-framework-mlcompute" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/c9/22fe4720685724ec1444c8e5cdb41d360b1434d0971fb3e43cf3e9bf51fd/pyobjc_framework_mlcompute-11.0.tar.gz", hash = "sha256:1a1ee9ab43d1824300055ff94b042a26f38f1d18f6f0aa08be1c88278e7284d9", size = 89265, upload-time = "2025-01-14T19:04:43.326Z" } wheels = [ @@ -3485,9 +3489,9 @@ name = "pyobjc-framework-modelio" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/7c/b75b84d41e7854ffe9c9a42846f8105227a5fd0b02b690b4a75018b2caa3/pyobjc_framework_modelio-11.0.tar.gz", hash = "sha256:c875eb6ff7f94d18362a00faaa3016ae0c28140326338d18aa03c0b62f1c6b9d", size = 122652, upload-time = "2025-01-14T19:04:44.263Z" } wheels = [ @@ -3500,8 +3504,8 @@ name = "pyobjc-framework-multipeerconnectivity" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/14/80/4137cb9751aa3846c4954b3e61f948aae17afeb6851e01194aa50683caef/pyobjc_framework_multipeerconnectivity-11.0.tar.gz", hash = "sha256:8278a3483c0b6b88a8888ca76c46fd85808f9df56d45708cbc4e4182a5565cd3", size = 25534, upload-time = "2025-01-14T19:04:45.211Z" } wheels = [ @@ -3514,8 +3518,8 @@ name = "pyobjc-framework-naturallanguage" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/62/64/63e97635fa637384bc8c980796573dc7a9e7074a6866aef073b1faf3e11d/pyobjc_framework_naturallanguage-11.0.tar.gz", hash = "sha256:4c9471fa2c48a8fd4899de4406823e66cb0292dbba7b471622017f3647d53fa4", size = 46385, upload-time = "2025-01-14T19:04:46.185Z" } wheels = [ @@ -3528,8 +3532,8 @@ name = "pyobjc-framework-netfs" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/29/eb569870b52c7581104ed2806cae2d425d60b5ab304128cd58155d5b567f/pyobjc_framework_netfs-11.0.tar.gz", hash = "sha256:3de5f627a62addf4aab8a4d2d07213e9b2b6c8adbe6cc4c332ee868075785a6a", size = 16173, upload-time = "2025-01-14T19:04:47.11Z" } wheels = [ @@ -3542,8 +3546,8 @@ name = "pyobjc-framework-network" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/8e/18e55aff83549e041484d2ee94dd91b29cec9de40508e7fe9c4afec110a7/pyobjc_framework_network-11.0.tar.gz", hash = "sha256:d4dcc02773d7d642a385c7f0d951aeb7361277446c912a49230cddab60a65ab8", size = 124160, upload-time = "2025-01-14T19:04:50.191Z" } wheels = [ @@ -3556,8 +3560,8 @@ name = "pyobjc-framework-networkextension" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/59/90/97dcfac5895b07e891adf634c3a074b68992d132ccfab386c186ac1a598c/pyobjc_framework_networkextension-11.0.tar.gz", hash = "sha256:5ba2254e2c13010b6c4f1e2948047d95eff86bfddfc77716747718fa3a8cb1af", size = 188551, upload-time = "2025-01-14T19:04:51.352Z" } wheels = [ @@ -3570,8 +3574,8 @@ name = "pyobjc-framework-notificationcenter" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d7/d0/f0a602e01173531a2b639e283a092cf1f307fd873abd2ed590b9c4122337/pyobjc_framework_notificationcenter-11.0.tar.gz", hash = "sha256:f878b318c693d63d6b8bd1c3e2ad4f8097b22872f18f40142e394d84f1ead9f6", size = 22844, upload-time = "2025-01-14T19:04:52.459Z" } wheels = [ @@ -3584,8 +3588,8 @@ name = "pyobjc-framework-opendirectory" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/cf/ba0cf807758acdc6a19e4787fdcda2eb59034aa22c4203d04fd49b276981/pyobjc_framework_opendirectory-11.0.tar.gz", hash = "sha256:0c82594f4f0bcf2318c4641527f9243962d7b03e67d4f3fb111b899a299fc7eb", size = 189165, upload-time = "2025-01-14T19:04:53.42Z" } wheels = [ @@ -3598,8 +3602,8 @@ name = "pyobjc-framework-osakit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d3/4a/e49680f7f3ab9c0632ed9be76a0a59299e7fd797335690b3da4d117f2d7b/pyobjc_framework_osakit-11.0.tar.gz", hash = "sha256:77ac18e2660133a9eeb01c76ad3df3b4b36fd29005fc36bca00f57cca121aac3", size = 22535, upload-time = "2025-01-14T19:04:54.753Z" } wheels = [ @@ -3612,10 +3616,10 @@ name = "pyobjc-framework-oslog" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b0/93/0a72353d0212a815bd5e43aec528ce7b28b71d461d26e5fa3882ff96ffa3/pyobjc_framework_oslog-11.0.tar.gz", hash = "sha256:9d29eb7c89a41d7c702dffb6e2e338a2d5219387c8dae22b67754ddf9e2fcb3f", size = 24151, upload-time = "2025-01-14T19:04:55.587Z" } wheels = [ @@ -3628,8 +3632,8 @@ name = "pyobjc-framework-passkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/f8/ebb2bc840f87292a4f60080463ee698ca08516cc958364741dfff2858b33/pyobjc_framework_passkit-11.0.tar.gz", hash = "sha256:2044d9d634dd98b7b624ee09487b27e5f26a7729f6689abba23a4a011febe19c", size = 120495, upload-time = "2025-01-14T19:04:57.689Z" } wheels = [ @@ -3642,8 +3646,8 @@ name = "pyobjc-framework-pencilkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f4/8d/1e97cd72b776e5e1294cbda84325b364702617dd435d32448dcc0a80bd93/pyobjc_framework_pencilkit-11.0.tar.gz", hash = "sha256:9598c28e83f5b7f091592cc1af2b16f7ae94cf00045d8d14ed2c17cb9e4ffd50", size = 22812, upload-time = "2025-01-14T19:04:58.652Z" } wheels = [ @@ -3656,8 +3660,8 @@ name = "pyobjc-framework-phase" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-avfoundation", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-avfoundation" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/a2/65182dcb44fceb2173f4134d6cd4325dfd0731225b621aa2027d2a03d043/pyobjc_framework_phase-11.0.tar.gz", hash = "sha256:e06a0f8308ae4f3731f88b3e1239b7bdfdda3eef97023e3ce972e2f386451d80", size = 59214, upload-time = "2025-01-14T19:04:59.461Z" } wheels = [ @@ -3670,8 +3674,8 @@ name = "pyobjc-framework-photos" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f7/c3/fc755c1f8f411433d7ba2e92f3fe3e7b417e9629675ad6baf94ac8b01e64/pyobjc_framework_photos-11.0.tar.gz", hash = "sha256:cfdfdefb0d560b091425227d5c0e24a40b445b5251ff4d37bd326cd8626b80cd", size = 92122, upload-time = "2025-01-14T19:05:01.804Z" } wheels = [ @@ -3684,8 +3688,8 @@ name = "pyobjc-framework-photosui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/2c/70ac99fb2b7ba14d220c78cf6401c0c7a47992269f85f699220a6a2cff09/pyobjc_framework_photosui-11.0.tar.gz", hash = "sha256:3c65342e31f6109d8229992b2712b29cab1021475969b55f4f215dd97e2a99db", size = 47898, upload-time = "2025-01-14T19:05:02.737Z" } wheels = [ @@ -3698,8 +3702,8 @@ name = "pyobjc-framework-preferencepanes" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/35/01/81cc46e0a92d15f2b664b2efdcc8fd310acac570c9f63a99d446e0489784/pyobjc_framework_preferencepanes-11.0.tar.gz", hash = "sha256:ee000c351befeb81f4fa678ada85695ca4af07933b6bd9b1947164e16dd0b3e5", size = 26419, upload-time = "2025-01-14T19:05:03.787Z" } wheels = [ @@ -3712,8 +3716,8 @@ name = "pyobjc-framework-pushkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/17/ab/7fe55ce5b32c434142be026ec27b1801a2d4694b159b502f9ecd568eebf2/pyobjc_framework_pushkit-11.0.tar.gz", hash = "sha256:df9854ed4065c50022863b3c11c2a21c4279b36c2b5c8f08b834174aacb44e81", size = 20816, upload-time = "2025-01-14T19:05:05.468Z" } wheels = [ @@ -3726,8 +3730,8 @@ name = "pyobjc-framework-quartz" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a5/ad/f00f3f53387c23bbf4e0bb1410e11978cbf87c82fa6baff0ee86f74c5fb6/pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619", size = 3952463, upload-time = "2025-01-14T19:05:07.931Z" } wheels = [ @@ -3740,9 +3744,9 @@ name = "pyobjc-framework-quicklookthumbnailing" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/a1/35ca40d2d4ab05acbc9766986d482482d466529003711c7b4e52a8df4935/pyobjc_framework_quicklookthumbnailing-11.0.tar.gz", hash = "sha256:40763284bd0f71e6a55803f5234ad9cd8e8dd3aaaf5e1fd204e6c952b3f3530d", size = 16784, upload-time = "2025-01-14T19:05:09.857Z" } wheels = [ @@ -3755,8 +3759,8 @@ name = "pyobjc-framework-replaykit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/aa/43/c751c517dbb8ee599a31e59832c01080473c7964b6996ca29906f46c0967/pyobjc_framework_replaykit-11.0.tar.gz", hash = "sha256:e5693589423eb9ad99d63a7395169f97b484a58108321877b0fc27c748344593", size = 25589, upload-time = "2025-01-14T19:05:10.791Z" } wheels = [ @@ -3769,8 +3773,8 @@ name = "pyobjc-framework-safariservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/ec/c9a97b1aa713145cc8c522c4146af06b293cfe1a959a03ee91007949533b/pyobjc_framework_safariservices-11.0.tar.gz", hash = "sha256:dba416bd0ed5f4481bc400bf56ce57e982c19feaae94bc4eb75d8bda9af15b7e", size = 34367, upload-time = "2025-01-14T19:05:12.914Z" } wheels = [ @@ -3783,9 +3787,9 @@ name = "pyobjc-framework-safetykit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4e/30/89bfdbdca93e57b19891ddeff1742b20a2019cdeb2e44902027dce2642e1/pyobjc_framework_safetykit-11.0.tar.gz", hash = "sha256:9ec996a6a8eecada4b9fd1138244bcffea96a37722531f0ec16566049dfd4cdb", size = 20745, upload-time = "2025-01-14T19:05:13.925Z" } wheels = [ @@ -3798,9 +3802,9 @@ name = "pyobjc-framework-scenekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/3f/a2761585399e752bce8275c9d56990d4b83e57b13d06dd98335891176a89/pyobjc_framework_scenekit-11.0.tar.gz", hash = "sha256:c0f37019f8de2a583f66e6d14dfd4ae23c8d8703e93f61c1c91728a21f62cd26", size = 213647, upload-time = "2025-01-14T19:05:15.129Z" } wheels = [ @@ -3813,9 +3817,9 @@ name = "pyobjc-framework-screencapturekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coremedia" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/90/71f10db2f52ea324f82eaccc959442c43d21778cc5b1294c29e1942e635c/pyobjc_framework_screencapturekit-11.0.tar.gz", hash = "sha256:ca2c960e28216e56f33e4ca9b9b1eda12d9c17b719bae727181e8b96f0314c4b", size = 53046, upload-time = "2025-01-14T19:05:16.834Z" } wheels = [ @@ -3828,8 +3832,8 @@ name = "pyobjc-framework-screensaver" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f6/b6/71c20259a1bfffcb5103be62564006b1bbc21f80180658101e2370683bcb/pyobjc_framework_screensaver-11.0.tar.gz", hash = "sha256:2e4c643624cc0cffeafc535c43faf5f8de8be030307fa8a5bea257845e8af474", size = 23774, upload-time = "2025-01-14T19:05:19.325Z" } wheels = [ @@ -3842,8 +3846,8 @@ name = "pyobjc-framework-screentime" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/a7/ee60ee5b0471a4367eaa1c8a243418874fd48fac5dbdfdd318a653d94aaa/pyobjc_framework_screentime-11.0.tar.gz", hash = "sha256:6dd74dc64be1865346fcff63b8849253697f7ac68d83ee2708019cf3852c1cd7", size = 14398, upload-time = "2025-01-14T19:05:21.547Z" } wheels = [ @@ -3856,8 +3860,8 @@ name = "pyobjc-framework-scriptingbridge" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/f0/592af19047935e44c07ddd1eba4f05aa8eb460ee842f7d5d48501231cd69/pyobjc_framework_scriptingbridge-11.0.tar.gz", hash = "sha256:65e5edd0ea608ae7f01808b963dfa25743315f563705d75c493c2fa7032f88cc", size = 22626, upload-time = "2025-01-14T19:05:22.461Z" } wheels = [ @@ -3870,8 +3874,8 @@ name = "pyobjc-framework-searchkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreservices", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-coreservices" }, ] sdist = { url = "https://files.pythonhosted.org/packages/15/27/9676327cf7d13346d546325b411a5deaa072bd0fbe733c8aae8a9a00c0e0/pyobjc_framework_searchkit-11.0.tar.gz", hash = "sha256:36f3109e74bc5e6fab60c02be804d5ed1c511ad51ea0d597a6c6a9653573ddf5", size = 31182, upload-time = "2025-01-14T19:05:24.667Z" } wheels = [ @@ -3884,8 +3888,8 @@ name = "pyobjc-framework-security" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c5/75/4b916bff8c650e387077a35916b7a7d331d5ff03bed7275099d96dcc6cd9/pyobjc_framework_security-11.0.tar.gz", hash = "sha256:ac078bb9cc6762d6f0f25f68325dcd7fe77acdd8c364bf4378868493f06a0758", size = 347059, upload-time = "2025-01-14T19:05:26.17Z" } wheels = [ @@ -3898,9 +3902,9 @@ name = "pyobjc-framework-securityfoundation" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-security" }, ] sdist = { url = "https://files.pythonhosted.org/packages/84/d6/0d817edb11d2bdb0f536059e913191e587f1984e39397bb3341209d92c21/pyobjc_framework_securityfoundation-11.0.tar.gz", hash = "sha256:5ae906ded5dd40046c013a7e0c1f59416abafb4b72bc947b6cd259749745e637", size = 13526, upload-time = "2025-01-14T19:05:27.275Z" } wheels = [ @@ -3913,9 +3917,9 @@ name = "pyobjc-framework-securityinterface" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-security", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-security" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/88/d7c4942650707fe5b1d3b45b42684f58f2cab7d2772ec74ca96ecef575eb/pyobjc_framework_securityinterface-11.0.tar.gz", hash = "sha256:8843a27cf30a8e4dd6e2cb7702a6d65ad4222429f0ccc6c062537af4683b1c08", size = 37118, upload-time = "2025-01-14T19:05:28.569Z" } wheels = [ @@ -3928,9 +3932,9 @@ name = "pyobjc-framework-sensitivecontentanalysis" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/00/e4/f1e0f150ae6c6ad7dde9b248f34f324f4f8b1c42260dbf62420f80d79ba9/pyobjc_framework_sensitivecontentanalysis-11.0.tar.gz", hash = "sha256:0f09034688f894c0f4409c16adaf857d78714d55472de4aa2ac40fbd7ba233d6", size = 13060, upload-time = "2025-01-14T19:05:29.655Z" } wheels = [ @@ -3943,8 +3947,8 @@ name = "pyobjc-framework-servicemanagement" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1b/59/8d38b5cdbcfb57ab842e080436dbd04d5a5d2080e99a2ea1e286cfad12a8/pyobjc_framework_servicemanagement-11.0.tar.gz", hash = "sha256:10b1bbcee3de5bb2b9fc3d6763eb682b7a1d9ddd4bd2c882fece62783cb17885", size = 16882, upload-time = "2025-01-14T19:05:30.537Z" } wheels = [ @@ -3957,8 +3961,8 @@ name = "pyobjc-framework-sharedwithyou" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-sharedwithyoucore", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-sharedwithyoucore" }, ] sdist = { url = "https://files.pythonhosted.org/packages/20/84/db667061f815537717a6cac891df01a45b65e6feaa2dfa0c9d2e3803a1ef/pyobjc_framework_sharedwithyou-11.0.tar.gz", hash = "sha256:a3a03daac77ad7364ed22109ca90c6cd2dcb7611a96cbdf37d30543ef1579399", size = 33696, upload-time = "2025-01-14T19:05:31.396Z" } wheels = [ @@ -3971,8 +3975,8 @@ name = "pyobjc-framework-sharedwithyoucore" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/52/2a/86904cd9cc3bf5cdb9101481e17e67358f39f81ffa0f36768097287e34b3/pyobjc_framework_sharedwithyoucore-11.0.tar.gz", hash = "sha256:3932452677df5d67ea27845ab26ccaaa1d1779196bf16b62c5655f13d822c82d", size = 28877, upload-time = "2025-01-14T19:05:32.283Z" } wheels = [ @@ -3985,8 +3989,8 @@ name = "pyobjc-framework-shazamkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dd/2a/1f4ad92260860e500cb61119e8e7fe604b0788c32f5b00446b5a56705a2b/pyobjc_framework_shazamkit-11.0.tar.gz", hash = "sha256:cea736cefe90b6bb989d0a8abdc21ef4b3b431b27657abb09d6deb0b2c1bd37a", size = 25172, upload-time = "2025-01-14T19:05:34.497Z" } wheels = [ @@ -3999,8 +4003,8 @@ name = "pyobjc-framework-social" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/56/ed483f85105ef929241ab1a6ed3dbfd0be558bb900e36b274f997db9c869/pyobjc_framework_social-11.0.tar.gz", hash = "sha256:ccedd6eddb6744049467bce19b4ec4f0667ec60552731c02dcbfa8938a3ac798", size = 14806, upload-time = "2025-01-14T19:05:35.394Z" } wheels = [ @@ -4013,8 +4017,8 @@ name = "pyobjc-framework-soundanalysis" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/14/697ca1b76228a96bb459f3cf43234798b05fdf11691202449d98d9d887af/pyobjc_framework_soundanalysis-11.0.tar.gz", hash = "sha256:f541fcd04ec5d7528dd2ae2d873a92a3092e87fb70b8df229c79defb4d807d1a", size = 16789, upload-time = "2025-01-14T19:05:36.576Z" } wheels = [ @@ -4027,8 +4031,8 @@ name = "pyobjc-framework-speech" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5f/39/e9f0a73243c38d85f8da6a1a2afda73503e2fcc31a72f5479770bceae0c1/pyobjc_framework_speech-11.0.tar.gz", hash = "sha256:92a191c3ecfe7032eea2140ab5dda826a59c7bb84b13a2edb0ebc471a76e6d7b", size = 40620, upload-time = "2025-01-14T19:05:38.391Z" } wheels = [ @@ -4041,9 +4045,9 @@ name = "pyobjc-framework-spritekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b7/6e/642e64f5b62a7777c784931c7f018788b5620e307907d416c837fd0c4315/pyobjc_framework_spritekit-11.0.tar.gz", hash = "sha256:aa43927e325d4ac253b7c0ec4df95393b0354bd278ebe9871803419d12d1ef80", size = 129851, upload-time = "2025-01-14T19:05:39.709Z" } wheels = [ @@ -4056,8 +4060,8 @@ name = "pyobjc-framework-storekit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/ca/f4e5a1ff8c98bbbf208639b2bef7bf3b88936bccda1d8ed34aa7d052f589/pyobjc_framework_storekit-11.0.tar.gz", hash = "sha256:ef7e75b28f1fa8b0b6413e64b9d5d78b8ca358fc2477483d2783f688ff8d75e0", size = 75855, upload-time = "2025-01-14T19:05:41.605Z" } wheels = [ @@ -4070,8 +4074,8 @@ name = "pyobjc-framework-symbols" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dc/92/a20a3d7af3c99e0ea086e43715675160a04b86c1d069bdaeb3acdb015d92/pyobjc_framework_symbols-11.0.tar.gz", hash = "sha256:e3de7736dfb8107f515cfd23f03e874dd9468e88ab076d01d922a73fefb620fa", size = 13682, upload-time = "2025-01-14T19:05:45.727Z" } wheels = [ @@ -4084,9 +4088,9 @@ name = "pyobjc-framework-syncservices" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coredata", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coredata" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/22/642186906f672461bab1d7773b35ef74e432b9789ca2248186b766e9fd3b/pyobjc_framework_syncservices-11.0.tar.gz", hash = "sha256:7867c23895a8289da8d56e962c144c36ed16bd101dc07d05281c55930b142471", size = 57453, upload-time = "2025-01-14T19:05:46.559Z" } wheels = [ @@ -4099,8 +4103,8 @@ name = "pyobjc-framework-systemconfiguration" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/70/70/ebebf311523f436df2407f35d7ce62482c01e530b77aceb3ca6356dcef43/pyobjc_framework_systemconfiguration-11.0.tar.gz", hash = "sha256:06487f0fdd43c6447b5fd3d7f3f59826178d32bcf74f848c5b3ea597191d471d", size = 142949, upload-time = "2025-01-14T19:05:47.466Z" } wheels = [ @@ -4113,8 +4117,8 @@ name = "pyobjc-framework-systemextensions" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/62/4b/904d818debf6216b7be009d492d998c819bf2f2791bfb75870a952e32cf9/pyobjc_framework_systemextensions-11.0.tar.gz", hash = "sha256:da293c99b428fb7f18a7a1d311b17177f73a20c7ffa94de3f72d760df924255e", size = 22531, upload-time = "2025-01-14T19:05:48.463Z" } wheels = [ @@ -4127,8 +4131,8 @@ name = "pyobjc-framework-threadnetwork" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c4/17/fc8fde4eeb6697e0a5ba1a306cd62d3a95b53f3334744cd22b87037d8a14/pyobjc_framework_threadnetwork-11.0.tar.gz", hash = "sha256:f5713579380f6fb89c877796de86cb4e98428d7a9cbfebe566fb827ba23b2d8e", size = 13820, upload-time = "2025-01-14T19:05:49.307Z" } wheels = [ @@ -4141,8 +4145,8 @@ name = "pyobjc-framework-uniformtypeidentifiers" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/56/4f/fd571c1f87d5ee3d86c4d2008806e9623d2662bbc788d9001b3fff35275f/pyobjc_framework_uniformtypeidentifiers-11.0.tar.gz", hash = "sha256:6ae6927a3ed1f0197a8c472226f11f46ccd5ed398b4449613e1d10346d9ed15d", size = 20860, upload-time = "2025-01-14T19:05:50.073Z" } wheels = [ @@ -4155,8 +4159,8 @@ name = "pyobjc-framework-usernotifications" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/f5/ca3e6a7d940b3aca4323e4f5409b14b5d2eb45432158430c584e3800ce4d/pyobjc_framework_usernotifications-11.0.tar.gz", hash = "sha256:7950a1c6a8297f006c26c3d286705ffc2a07061d6e844f1106290572097b872c", size = 54857, upload-time = "2025-01-14T19:05:52.42Z" } wheels = [ @@ -4169,9 +4173,9 @@ name = "pyobjc-framework-usernotificationsui" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-usernotifications", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-usernotifications" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/e8/f0d50cdc678260a628b92e55b5752155f941c2f72b96fe3f2412a28c5d79/pyobjc_framework_usernotificationsui-11.0.tar.gz", hash = "sha256:d0ec597d189b4d228b0b836474aef318652c1c287b33442a1403c49dc59fdb7f", size = 14369, upload-time = "2025-01-14T19:05:54.498Z" } wheels = [ @@ -4184,8 +4188,8 @@ name = "pyobjc-framework-videosubscriberaccount" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/2e/6a7debd84911a9384b4e7a9cc3f308e3461a00a9d74f33b153bdd872f15f/pyobjc_framework_videosubscriberaccount-11.0.tar.gz", hash = "sha256:163b32f361f48b9d20f317461464abd4427b3242693ae011633fc443c7d5449c", size = 29100, upload-time = "2025-01-14T19:05:55.319Z" } wheels = [ @@ -4198,10 +4202,10 @@ name = "pyobjc-framework-videotoolbox" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coremedia", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coremedia" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ba/2d/c031a132b142fcd20846cc1ac3ba92abaa58ec04164fd36ca978d9374f1c/pyobjc_framework_videotoolbox-11.0.tar.gz", hash = "sha256:a54ed8f8bcbdd2bdea2a296dc02a8a7d42f81e2b6ccbf4d1f10cec5e7a09bec0", size = 81157, upload-time = "2025-01-14T19:05:56.135Z" } wheels = [ @@ -4214,8 +4218,8 @@ name = "pyobjc-framework-virtualization" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/65/8d/e57e1f2c5ac950dc3da6c977effde4a55b8b70424b1bdb97b5530559f5bc/pyobjc_framework_virtualization-11.0.tar.gz", hash = "sha256:03e1c1fa20950aa7c275e5f11f1257108b6d1c6a7403afb86f4e9d5fae87b73c", size = 78144, upload-time = "2025-01-14T19:05:57.086Z" } wheels = [ @@ -4228,10 +4232,10 @@ name = "pyobjc-framework-vision" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreml", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coreml" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/53/dc2e0562a177af9306efceb84bc21f5cf7470acaa8f28f64e62bf828b7e1/pyobjc_framework_vision-11.0.tar.gz", hash = "sha256:45342e5253c306dbcd056a68bff04ffbfa00e9ac300a02aabf2e81053b771e39", size = 133175, upload-time = "2025-01-14T19:05:58.013Z" } wheels = [ @@ -4244,8 +4248,8 @@ name = "pyobjc-framework-webkit" version = "11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/4f/02a6270acf225c2a34339677e796002c77506238475059ae6e855358a40c/pyobjc_framework_webkit-11.0.tar.gz", hash = "sha256:fa6bedf9873786b3376a74ce2ea9dcd311f2a80f61e33dcbd931cc956aa29644", size = 767210, upload-time = "2025-01-14T19:05:59.3Z" } wheels = [ @@ -4258,9 +4262,9 @@ name = "pyopencl" version = "2025.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "pytools", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy" }, + { name = "platformdirs" }, + { name = "pytools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/88/0ac460d3e2def08b2ad6345db6a13613815f616bbbd60c6f4bdf774f4c41/pyopencl-2025.1.tar.gz", hash = "sha256:0116736d7f7920f87b8db4b66a03f27b1d930d2e37ddd14518407cc22dd24779", size = 422510, upload-time = "2025-01-22T00:16:58.421Z" } wheels = [ @@ -4436,14 +4440,14 @@ wheels = [ [[package]] name = "pytest-timeout" -version = "2.3.1" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/93/0d/04719abc7a4bdb3a7a1f968f24b0f5253d698c9cc94975330e9d3145befb/pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9", size = 17697, upload-time = "2024-03-07T21:04:01.069Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/27/14af9ef8321f5edc7527e47def2a21d8118c6f329a9342cc61387a0c0599/pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e", size = 14148, upload-time = "2024-03-07T21:03:58.764Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, ] [[package]] @@ -4476,7 +4480,7 @@ name = "python-xlib" version = "0.33" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", marker = "sys_platform != 'darwin'" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32", size = 269068, upload-time = "2022-12-25T18:53:00.824Z" } wheels = [ @@ -4494,9 +4498,9 @@ name = "pytools" version = "2024.1.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "siphash24", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, - { name = "typing-extensions", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "platformdirs" }, + { name = "siphash24" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ee/0f/56e109c0307f831b5d598ad73976aaaa84b4d0e98da29a642e797eaa940c/pytools-2024.1.10.tar.gz", hash = "sha256:9af6f4b045212c49be32bb31fe19606c478ee4b09631886d05a32459f4ce0a12", size = 81741, upload-time = "2024-07-17T18:47:38.287Z" } wheels = [ @@ -4582,14 +4586,14 @@ wheels = [ [[package]] name = "pyyaml-env-tag" -version = "0.1" +version = "1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631, upload-time = "2020-11-12T02:38:26.239Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/95/32c8c79d784552ed687c676924381c0dc88b2a0248b50a32f4b5ac0ba03c/pyyaml_env_tag-1.0.tar.gz", hash = "sha256:bc952534a872b583f66f916e2dd83e7a7b9087847f4afca6d9c957c48b258ed2", size = 4462, upload-time = "2025-05-09T18:09:14.1Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911, upload-time = "2020-11-12T02:38:24.638Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8c/c35fdb193c3717bdb4dea0ea361dbe81997164e01deaa2809cc2d71aa6b6/pyyaml_env_tag-1.0-py3-none-any.whl", hash = "sha256:37f081041b8dca44ed8eb931ce0056f97de17251450f0ed08773dc2bcaf9e683", size = 4681, upload-time = "2025-05-09T18:09:12.611Z" }, ] [[package]] @@ -4671,7 +4675,7 @@ wheels = [ [[package]] name = "rerun-sdk" -version = "0.23.1" +version = "0.23.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -4681,11 +4685,11 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/6e/a125f4fe2de3269f443b7cb65d465ffd37a836a2dac7e4318e21239d78c8/rerun_sdk-0.23.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fe06d21cfcf4d84a9396f421d4779efabec7e9674d232a2c552c8a91d871c375", size = 66094053, upload-time = "2025-04-25T13:15:48.669Z" }, - { url = "https://files.pythonhosted.org/packages/55/f6/b6d13322b05dc77bd9a0127e98155c2b7ee987a236fd4d331eed2e547a90/rerun_sdk-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:823ae87bfa644e06fb70bada08a83690dd23d9824a013947f80a22c6731bdc0d", size = 62047843, upload-time = "2025-04-25T13:15:54.48Z" }, - { url = "https://files.pythonhosted.org/packages/a5/7f/6a7422cb727e14a65b55b0089988eeea8d0532c429397a863e6ba395554a/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:dc5129f8744f71249bf45558c853422c51ef39b6b5eea0ea1f602c6049ce732f", size = 68214509, upload-time = "2025-04-25T13:15:59.339Z" }, - { url = "https://files.pythonhosted.org/packages/4f/86/3aee9eadbfe55188a2c7d739378545b4319772a4d3b165e8d3fc598fa630/rerun_sdk-0.23.1-cp39-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:ee0d0e17df0e08be13b77cc74884c5d8ba8edb39b6f5a60dc2429d39033d90f6", size = 71442196, upload-time = "2025-04-25T13:16:04.405Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ba/028bd382e2ae21e6643cec25f423285dbc6b328ce56d55727b4101ef9443/rerun_sdk-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:d4273db55b56310b053a2de6bf5927a8692cf65f4d234c6e6928fb24ed8a960d", size = 57583198, upload-time = "2025-04-25T13:16:08.905Z" }, + { url = "https://files.pythonhosted.org/packages/24/98/ee9acc4ac36e977bcacd3d65704127026a02228f4f5efa9cfd4243dd2036/rerun_sdk-0.23.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:fb9f0279a8b87f64bc29ed2c4c3cbb602e36b9b373f691ff657968ed6009c5c3", size = 66816731, upload-time = "2025-05-06T15:54:46.177Z" }, + { url = "https://files.pythonhosted.org/packages/3a/84/6164958c9b4dcd9b9dab16e1adbf4cea4339f52ce212c084b57bc0fd1a05/rerun_sdk-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:02d99f48659abeca86fcbd56498d30ab0c4b34b8a0732b02324c3fe79830cf6f", size = 62697629, upload-time = "2025-05-06T15:54:52.083Z" }, + { url = "https://files.pythonhosted.org/packages/c0/4d/1b9dc66a5827f25f9ce5fe9b719f97f42403d1a2276e765082d224590dbe/rerun_sdk-0.23.2-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:fdc844eddb6dcc924e48b3cd8dd88ed8b70751301d386175dd2fd7c9594580b3", size = 68935354, upload-time = "2025-05-06T15:54:56.27Z" }, + { url = "https://files.pythonhosted.org/packages/e7/7b/16b9bcd67b3dea1842d556100ce2e0b5c6bd9ad7943aeab2a76bae4b2be5/rerun_sdk-0.23.2-cp39-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:dac599ef5d5563f4ebc90c20f09b77c458bac4df845e178112257f79f3eef579", size = 72209426, upload-time = "2025-05-06T15:55:00.641Z" }, + { url = "https://files.pythonhosted.org/packages/67/54/ecb761555f6fbd4e32c384ba5a8424c761925be54259708093205c916c64/rerun_sdk-0.23.2-cp39-abi3-win_amd64.whl", hash = "sha256:ce3f9adfb04df79f30ca1e6deb997b021af87878672f79dfa3543a4f6a83491f", size = 58180134, upload-time = "2025-05-06T15:55:06.284Z" }, ] [[package]] @@ -4737,27 +4741,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.11.8" +version = "0.11.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f6/adcf73711f31c9f5393862b4281c875a462d9f639f4ccdf69dc368311c20/ruff-0.11.8.tar.gz", hash = "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8", size = 4086399, upload-time = "2025-05-01T14:53:24.459Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/e7/e55dda1c92cdcf34b677ebef17486669800de01e887b7831a1b8fdf5cb08/ruff-0.11.9.tar.gz", hash = "sha256:ebd58d4f67a00afb3a30bf7d383e52d0e036e6195143c6db7019604a05335517", size = 4132134, upload-time = "2025-05-09T16:19:41.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/60/c6aa9062fa518a9f86cb0b85248245cddcd892a125ca00441df77d79ef88/ruff-0.11.8-py3-none-linux_armv6l.whl", hash = "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3", size = 10272473, upload-time = "2025-05-01T14:52:37.252Z" }, - { url = "https://files.pythonhosted.org/packages/a0/e4/0325e50d106dc87c00695f7bcd5044c6d252ed5120ebf423773e00270f50/ruff-0.11.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835", size = 11040862, upload-time = "2025-05-01T14:52:41.022Z" }, - { url = "https://files.pythonhosted.org/packages/e6/27/b87ea1a7be37fef0adbc7fd987abbf90b6607d96aa3fc67e2c5b858e1e53/ruff-0.11.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c", size = 10385273, upload-time = "2025-05-01T14:52:43.551Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f7/3346161570d789045ed47a86110183f6ac3af0e94e7fd682772d89f7f1a1/ruff-0.11.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c", size = 10578330, upload-time = "2025-05-01T14:52:45.48Z" }, - { url = "https://files.pythonhosted.org/packages/c6/c3/327fb950b4763c7b3784f91d3038ef10c13b2d42322d4ade5ce13a2f9edb/ruff-0.11.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219", size = 10122223, upload-time = "2025-05-01T14:52:47.675Z" }, - { url = "https://files.pythonhosted.org/packages/de/c7/ba686bce9adfeb6c61cb1bbadc17d58110fe1d602f199d79d4c880170f19/ruff-0.11.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f", size = 11697353, upload-time = "2025-05-01T14:52:50.264Z" }, - { url = "https://files.pythonhosted.org/packages/53/8e/a4fb4a1ddde3c59e73996bb3ac51844ff93384d533629434b1def7a336b0/ruff-0.11.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474", size = 12375936, upload-time = "2025-05-01T14:52:52.394Z" }, - { url = "https://files.pythonhosted.org/packages/ad/a1/9529cb1e2936e2479a51aeb011307e7229225df9ac64ae064d91ead54571/ruff-0.11.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38", size = 11850083, upload-time = "2025-05-01T14:52:55.424Z" }, - { url = "https://files.pythonhosted.org/packages/3e/94/8f7eac4c612673ae15a4ad2bc0ee62e03c68a2d4f458daae3de0e47c67ba/ruff-0.11.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458", size = 14005834, upload-time = "2025-05-01T14:52:58.056Z" }, - { url = "https://files.pythonhosted.org/packages/1e/7c/6f63b46b2be870cbf3f54c9c4154d13fac4b8827f22fa05ac835c10835b2/ruff-0.11.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5", size = 11503713, upload-time = "2025-05-01T14:53:01.244Z" }, - { url = "https://files.pythonhosted.org/packages/3a/91/57de411b544b5fe072779678986a021d87c3ee5b89551f2ca41200c5d643/ruff-0.11.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948", size = 10457182, upload-time = "2025-05-01T14:53:03.726Z" }, - { url = "https://files.pythonhosted.org/packages/01/49/cfe73e0ce5ecdd3e6f1137bf1f1be03dcc819d1bfe5cff33deb40c5926db/ruff-0.11.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb", size = 10101027, upload-time = "2025-05-01T14:53:06.555Z" }, - { url = "https://files.pythonhosted.org/packages/56/21/a5cfe47c62b3531675795f38a0ef1c52ff8de62eaddf370d46634391a3fb/ruff-0.11.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c", size = 11111298, upload-time = "2025-05-01T14:53:08.825Z" }, - { url = "https://files.pythonhosted.org/packages/36/98/f76225f87e88f7cb669ae92c062b11c0a1e91f32705f829bd426f8e48b7b/ruff-0.11.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304", size = 11566884, upload-time = "2025-05-01T14:53:11.626Z" }, - { url = "https://files.pythonhosted.org/packages/de/7e/fff70b02e57852fda17bd43f99dda37b9bcf3e1af3d97c5834ff48d04715/ruff-0.11.8-py3-none-win32.whl", hash = "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2", size = 10451102, upload-time = "2025-05-01T14:53:14.303Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a9/eaa571eb70648c9bde3120a1d5892597de57766e376b831b06e7c1e43945/ruff-0.11.8-py3-none-win_amd64.whl", hash = "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4", size = 11597410, upload-time = "2025-05-01T14:53:16.571Z" }, - { url = "https://files.pythonhosted.org/packages/cd/be/f6b790d6ae98f1f32c645f8540d5c96248b72343b0a56fab3a07f2941897/ruff-0.11.8-py3-none-win_arm64.whl", hash = "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2", size = 10713129, upload-time = "2025-05-01T14:53:22.27Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/75dfb7194fe6502708e547941d41162574d1f579c4676a8eb645bf1a6842/ruff-0.11.9-py3-none-linux_armv6l.whl", hash = "sha256:a31a1d143a5e6f499d1fb480f8e1e780b4dfdd580f86e05e87b835d22c5c6f8c", size = 10335453, upload-time = "2025-05-09T16:18:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/74/fc/ad80c869b1732f53c4232bbf341f33c5075b2c0fb3e488983eb55964076a/ruff-0.11.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:66bc18ca783b97186a1f3100e91e492615767ae0a3be584e1266aa9051990722", size = 11072566, upload-time = "2025-05-09T16:19:01.432Z" }, + { url = "https://files.pythonhosted.org/packages/87/0d/0ccececef8a0671dae155cbf7a1f90ea2dd1dba61405da60228bbe731d35/ruff-0.11.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bd576cd06962825de8aece49f28707662ada6a1ff2db848d1348e12c580acbf1", size = 10435020, upload-time = "2025-05-09T16:19:03.897Z" }, + { url = "https://files.pythonhosted.org/packages/52/01/e249e1da6ad722278094e183cbf22379a9bbe5f21a3e46cef24ccab76e22/ruff-0.11.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b1d18b4be8182cc6fddf859ce432cc9631556e9f371ada52f3eaefc10d878de", size = 10593935, upload-time = "2025-05-09T16:19:06.455Z" }, + { url = "https://files.pythonhosted.org/packages/ed/9a/40cf91f61e3003fe7bd43f1761882740e954506c5a0f9097b1cff861f04c/ruff-0.11.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f3f46f759ac623e94824b1e5a687a0df5cd7f5b00718ff9c24f0a894a683be7", size = 10172971, upload-time = "2025-05-09T16:19:10.261Z" }, + { url = "https://files.pythonhosted.org/packages/61/12/d395203de1e8717d7a2071b5a340422726d4736f44daf2290aad1085075f/ruff-0.11.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f34847eea11932d97b521450cf3e1d17863cfa5a94f21a056b93fb86f3f3dba2", size = 11748631, upload-time = "2025-05-09T16:19:12.307Z" }, + { url = "https://files.pythonhosted.org/packages/66/d6/ef4d5eba77677eab511644c37c55a3bb8dcac1cdeb331123fe342c9a16c9/ruff-0.11.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f33b15e00435773df97cddcd263578aa83af996b913721d86f47f4e0ee0ff271", size = 12409236, upload-time = "2025-05-09T16:19:15.006Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8f/5a2c5fc6124dd925a5faf90e1089ee9036462118b619068e5b65f8ea03df/ruff-0.11.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b27613a683b086f2aca8996f63cb3dd7bc49e6eccf590563221f7b43ded3f65", size = 11881436, upload-time = "2025-05-09T16:19:17.063Z" }, + { url = "https://files.pythonhosted.org/packages/39/d1/9683f469ae0b99b95ef99a56cfe8c8373c14eba26bd5c622150959ce9f64/ruff-0.11.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e0d88756e63e8302e630cee3ce2ffb77859797cc84a830a24473939e6da3ca6", size = 13982759, upload-time = "2025-05-09T16:19:19.693Z" }, + { url = "https://files.pythonhosted.org/packages/4e/0b/c53a664f06e0faab596397867c6320c3816df479e888fe3af63bc3f89699/ruff-0.11.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:537c82c9829d7811e3aa680205f94c81a2958a122ac391c0eb60336ace741a70", size = 11541985, upload-time = "2025-05-09T16:19:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/23/a0/156c4d7e685f6526a636a60986ee4a3c09c8c4e2a49b9a08c9913f46c139/ruff-0.11.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:440ac6a7029f3dee7d46ab7de6f54b19e34c2b090bb4f2480d0a2d635228f381", size = 10465775, upload-time = "2025-05-09T16:19:24.401Z" }, + { url = "https://files.pythonhosted.org/packages/43/d5/88b9a6534d9d4952c355e38eabc343df812f168a2c811dbce7d681aeb404/ruff-0.11.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:71c539bac63d0788a30227ed4d43b81353c89437d355fdc52e0cda4ce5651787", size = 10170957, upload-time = "2025-05-09T16:19:27.08Z" }, + { url = "https://files.pythonhosted.org/packages/f0/b8/2bd533bdaf469dc84b45815ab806784d561fab104d993a54e1852596d581/ruff-0.11.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c67117bc82457e4501473c5f5217d49d9222a360794bfb63968e09e70f340abd", size = 11143307, upload-time = "2025-05-09T16:19:29.462Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d9/43cfba291788459b9bfd4e09a0479aa94d05ab5021d381a502d61a807ec1/ruff-0.11.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e4b78454f97aa454586e8a5557facb40d683e74246c97372af3c2d76901d697b", size = 11603026, upload-time = "2025-05-09T16:19:31.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e6/7ed70048e89b01d728ccc950557a17ecf8df4127b08a56944b9d0bae61bc/ruff-0.11.9-py3-none-win32.whl", hash = "sha256:7fe1bc950e7d7b42caaee2a8a3bc27410547cc032c9558ee2e0f6d3b209e845a", size = 10548627, upload-time = "2025-05-09T16:19:33.657Z" }, + { url = "https://files.pythonhosted.org/packages/90/36/1da5d566271682ed10f436f732e5f75f926c17255c9c75cefb77d4bf8f10/ruff-0.11.9-py3-none-win_amd64.whl", hash = "sha256:52edaa4a6d70f8180343a5b7f030c7edd36ad180c9f4d224959c2d689962d964", size = 11634340, upload-time = "2025-05-09T16:19:35.815Z" }, + { url = "https://files.pythonhosted.org/packages/40/f7/70aad26e5877c8f7ee5b161c4c9fa0100e63fc4c944dc6d97b9c7e871417/ruff-0.11.9-py3-none-win_arm64.whl", hash = "sha256:bcf42689c22f2e240f496d0c183ef2c6f7b35e809f12c1db58f75d9aa8d630ca", size = 10741080, upload-time = "2025-05-09T16:19:39.605Z" }, ] [[package]] @@ -4771,15 +4775,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.27.0" +version = "2.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/b6/a92ae6fa6d7e6e536bc586776b1669b84fb724dfe21b8ff08297f2d7c969/sentry_sdk-2.27.0.tar.gz", hash = "sha256:90f4f883f9eff294aff59af3d58c2d1b64e3927b28d5ada2b9b41f5aeda47daf", size = 323556, upload-time = "2025-04-24T10:09:37.927Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/bb/6a41b2e0e9121bed4d2ec68d50568ab95c49f4744156a9bbb789c866c66d/sentry_sdk-2.28.0.tar.gz", hash = "sha256:14d2b73bc93afaf2a9412490329099e6217761cbab13b6ee8bc0e82927e1504e", size = 325052, upload-time = "2025-05-12T07:53:12.785Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/8b/fb496a45854e37930b57564a20fb8e90dd0f8b6add0491527c00f2163b00/sentry_sdk-2.27.0-py2.py3-none-any.whl", hash = "sha256:c58935bfff8af6a0856d37e8adebdbc7b3281c2b632ec823ef03cd108d216ff0", size = 340786, upload-time = "2025-04-24T10:09:35.897Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4e/b1575833094c088dfdef63fbca794518860fcbc8002aadf51ebe8b6a387f/sentry_sdk-2.28.0-py2.py3-none-any.whl", hash = "sha256:51496e6cb3cb625b99c8e08907c67a9112360259b0ef08470e532c3ab184a232", size = 341693, upload-time = "2025-05-12T07:53:10.882Z" }, ] [[package]] @@ -4816,11 +4820,11 @@ wheels = [ [[package]] name = "setuptools" -version = "80.3.1" +version = "80.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/dc/3976b322de9d2e87ed0007cf04cc7553969b6c7b3f48a565d0333748fbcd/setuptools-80.3.1.tar.gz", hash = "sha256:31e2c58dbb67c99c289f51c16d899afedae292b978f8051efaf6262d8212f927", size = 1315082, upload-time = "2025-05-04T18:47:04.397Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/0cc40fe41fd2adb80a2f388987f4f8db3c866c69e33e0b4c8b093fdf700e/setuptools-80.4.0.tar.gz", hash = "sha256:5a78f61820bc088c8e4add52932ae6b8cf423da2aff268c23f813cfbb13b4006", size = 1315008, upload-time = "2025-05-09T20:42:27.972Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/7e/5d8af3317ddbf9519b687bd1c39d8737fde07d97f54df65553faca5cffb1/setuptools-80.3.1-py3-none-any.whl", hash = "sha256:ea8e00d7992054c4c592aeb892f6ad51fe1b4d90cc6947cc45c45717c40ec537", size = 1201172, upload-time = "2025-05-04T18:47:02.575Z" }, + { url = "https://files.pythonhosted.org/packages/b1/93/dba5ed08c2e31ec7cdc2ce75705a484ef0be1a2fecac8a58272489349de8/setuptools-80.4.0-py3-none-any.whl", hash = "sha256:6cdc8cb9a7d590b237dbe4493614a9b75d0559b888047c1f67d49ba50fc3edb2", size = 1200812, upload-time = "2025-05-09T20:42:25.325Z" }, ] [[package]] @@ -4828,7 +4832,7 @@ name = "shapely" version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload-time = "2025-04-03T09:15:05.725Z" } wheels = [ @@ -4914,9 +4918,9 @@ wheels = [ [[package]] name = "spidev" -version = "3.6" +version = "3.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/d9/401c0a7be089e02826cf2c201f489876b601f15be100fe391ef9c2faed83/spidev-3.6.tar.gz", hash = "sha256:14dbc37594a4aaef85403ab617985d3c3ef464d62bc9b769ef552db53701115b", size = 11917, upload-time = "2022-11-03T20:39:08.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/99/dd50af8200e224ce9412ad01cdbeeb5b39b2d61acd72138f2b92c4a6d619/spidev-3.7.tar.gz", hash = "sha256:ce628a5ff489f45132679879bff5f455a66abf9751af01843850155b06ae92f0", size = 11616, upload-time = "2025-05-06T14:23:30.783Z" } [[package]] name = "sympy" @@ -5086,7 +5090,7 @@ name = "yapf" version = "0.43.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "platformdirs" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } wheels = [ From b5545a6736f7f2e1631afdb14e2b951c81cdf1be Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 13 May 2025 05:00:18 +0800 Subject: [PATCH 191/477] system/ui: confirm dialog always opens in fullscreen mode (#35192) confirm dialog always opens in fullscreen mode --- system/ui/widgets/confirm_dialog.py | 16 +++++----------- system/ui/widgets/network.py | 2 +- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index e5ca002ecf..7a25682e27 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -1,4 +1,5 @@ import pyray as rl +from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_text_box @@ -11,10 +12,9 @@ TEXT_AREA_HEIGHT_REDUCTION = 200 BACKGROUND_COLOR = rl.Color(27, 27, 27, 255) -def confirm_dialog(rect: rl.Rectangle, message: str, confirm_text: str, cancel_text: str = "Cancel") -> int: - # Calculate dialog position and size, centered within the parent rectangle - dialog_x = rect.x + (rect.width - DIALOG_WIDTH) / 2 - dialog_y = rect.y + (rect.height - DIALOG_HEIGHT) / 2 +def confirm_dialog(message: str, confirm_text: str, cancel_text: str = "Cancel") -> int: + dialog_x = (gui_app.width - DIALOG_WIDTH) / 2 + dialog_y = (gui_app.height - DIALOG_HEIGHT) / 2 dialog_rect = rl.Rectangle(dialog_x, dialog_y, DIALOG_WIDTH, DIALOG_HEIGHT) # Calculate button positions at the bottom of the dialog @@ -27,13 +27,7 @@ def confirm_dialog(rect: rl.Rectangle, message: str, confirm_text: str, cancel_t yes_button = rl.Rectangle(yes_button_x, button_y, button_width, BUTTON_HEIGHT) # Draw the dialog background - rl.draw_rectangle( - int(dialog_rect.x), - int(dialog_rect.y), - int(dialog_rect.width), - int(dialog_rect.height), - BACKGROUND_COLOR, - ) + rl.draw_rectangle_rec(dialog_rect, BACKGROUND_COLOR) # Draw the message in the dialog, centered text_rect = rl.Rectangle(dialog_rect.x, dialog_rect.y, dialog_rect.width, dialog_rect.height - TEXT_AREA_HEIGHT_REDUCTION) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 06448483aa..d94a3facb1 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -75,7 +75,7 @@ class WifiManagerUI: self.state = StateIdle() case StateShowForgetConfirm(network): - result = confirm_dialog(rect, f'Forget Wi-Fi Network "{network.ssid}"?', "Forget") + result = confirm_dialog(f'Forget Wi-Fi Network "{network.ssid}"?', "Forget") if result == 1: self.forget_network(network) elif result == 0: From 2e5014d793f347254efe21c47532db3e6194dae6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 12 May 2025 17:23:58 -0700 Subject: [PATCH 192/477] Tesla: check counters and checksums in car interface (#35195) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index b922517802..e484515ec7 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit b9225178021be2b606dab89bf941440562f4cc6c +Subproject commit e484515ec7e329064b923869c1d2f15cf8c443a6 From 51ab7ec8f572dc5490c9d10dd6889d52cc1f427b Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 12 May 2025 18:56:13 -0700 Subject: [PATCH 193/477] feat(clip): ffmpeg improvements to reduce frame skips (#35196) * test * no benefit with two pass * rm * typo, testing * re-add --- tools/clip/run.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 12ac879baa..34efcdb5a8 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -177,9 +177,25 @@ def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, rou overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile=Inter.tff:fontcolor=white:fontsize=32:{box_style}:x=(w-text_w)/2:y=53") ffmpeg_cmd = [ - 'ffmpeg', '-y', '-video_size', RESOLUTION, '-framerate', str(FRAMERATE), '-f', 'x11grab', '-draw_mouse', '0', - '-i', display, '-c:v', 'libx264', '-maxrate', f'{bit_rate_kbps}k', '-bufsize', f'{bit_rate_kbps*2}k', '-crf', '23', - '-filter:v', ','.join(overlays), '-preset', 'ultrafast', '-pix_fmt', 'yuv420p', '-movflags', '+faststart', '-f', 'mp4', '-t', str(duration), out + 'ffmpeg', '-y', + '-video_size', RESOLUTION, + '-framerate', str(FRAMERATE), + '-f', 'x11grab', + '-rtbufsize', '100M', + '-draw_mouse', '0', + '-i', display, + '-c:v', 'libx264', + '-maxrate', f'{bit_rate_kbps}k', + '-bufsize', f'{bit_rate_kbps*2}k', + '-crf', '23', + '-filter:v', ','.join(overlays), + '-preset', 'ultrafast', + '-tune', 'zerolatency', + '-pix_fmt', 'yuv420p', + '-movflags', '+faststart', + '-f', 'mp4', + '-t', str(duration), + out, ] replay_cmd = [REPLAY, '-c', '1', '-s', str(begin_at), '--prefix', prefix] From 6b4436b9804e4149043bae81dbadd6f57680397c Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 12 May 2025 19:49:37 -0700 Subject: [PATCH 194/477] feat(clip): font from `selfdrive/assets` (#35194) * feat(clip): find font dynamically * check font path as part of validating environment * from testing * use font from assets * no mas --- tools/clip/run.py | 7 ++++--- tools/install_ubuntu_dependencies.sh | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 34efcdb5a8..5fa3c0ae69 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -28,6 +28,7 @@ RESOLUTION = '2160x1080' SECONDS_TO_WARM = 2 PROC_WAIT_SECONDS = 30 +OPENPILOT_FONT = str(Path(BASEDIR, 'selfdrive/assets/fonts/Inter-Regular.ttf').resolve()) REPLAY = str(Path(BASEDIR, 'tools/replay/replay').resolve()) UI = str(Path(BASEDIR, 'selfdrive/ui/ui').resolve()) @@ -169,12 +170,12 @@ def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, rou meta_text = get_meta_text(route) overlays = [ # metadata overlay - f"drawtext=text='{escape_ffmpeg_text(meta_text)}':fontfile=Inter.tff:fontcolor=white:fontsize=18:{box_style}:x=(w-text_w)/2:y=5.5:enable='between(t,1,5)'", + f"drawtext=text='{escape_ffmpeg_text(meta_text)}':fontfile={OPENPILOT_FONT}:fontcolor=white:fontsize=15:{box_style}:x=(w-text_w)/2:y=5.5:enable='between(t,1,5)'", # route time overlay - f"drawtext=text='%{{eif\\:floor(({start}+t)/60)\\:d\\:2}}\\:%{{eif\\:mod({start}+t\\,60)\\:d\\:2}}':fontfile=Inter.tff:fontcolor=white:fontsize=24:{box_style}:x=w-text_w-38:y=38" + f"drawtext=text='%{{eif\\:floor(({start}+t)/60)\\:d\\:2}}\\:%{{eif\\:mod({start}+t\\,60)\\:d\\:2}}':fontfile={OPENPILOT_FONT}:fontcolor=white:fontsize=24:{box_style}:x=w-text_w-38:y=38" ] if title: - overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile=Inter.tff:fontcolor=white:fontsize=32:{box_style}:x=(w-text_w)/2:y=53") + overlays.append(f"drawtext=text='{escape_ffmpeg_text(title)}':fontfile={OPENPILOT_FONT}:fontcolor=white:fontsize=32:{box_style}:x=(w-text_w)/2:y=53") ffmpeg_cmd = [ 'ffmpeg', '-y', diff --git a/tools/install_ubuntu_dependencies.sh b/tools/install_ubuntu_dependencies.sh index 3d6fd2e7a9..f33569704a 100755 --- a/tools/install_ubuntu_dependencies.sh +++ b/tools/install_ubuntu_dependencies.sh @@ -33,7 +33,6 @@ function install_ubuntu_common_requirements() { git \ git-lfs \ ffmpeg \ - fonts-inter \ libavformat-dev \ libavcodec-dev \ libavdevice-dev \ From 3f724400c58fb50d8c657ce0177d275ecaae788f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 12 May 2025 21:15:43 -0700 Subject: [PATCH 195/477] Tesla: check checksums in safety (#35198) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index e484515ec7..95ee4edd17 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit e484515ec7e329064b923869c1d2f15cf8c443a6 +Subproject commit 95ee4edd17ecc6700eac12f2074de6b5478b9477 From 8473774b6581ff2567e9ca3904589aa41432a1dd Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 12 May 2025 21:18:51 -0700 Subject: [PATCH 196/477] process replay: add Tesla route (#35199) * add tesla * update refs * run tesla on more procs * update refs --- selfdrive/test/process_replay/ref_commit | 2 +- selfdrive/test/process_replay/test_processes.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 1ae4c4a1a8..f5cd05b16e 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -7bf4ae5b92a3ad1f073f675e24e28babad0f2aa0 +b31b7c5c29e6d30ccee2fa5105af778810fcd02e \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index d479369693..a82eab27dd 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -35,6 +35,7 @@ source_segments = [ ("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.MAZDA_CX9_2021 ("FORD", "54827bf84c38b14f|2023-01-26--21-59-07--4"), # FORD.FORD_BRONCO_SPORT_MK1 ("RIVIAN", "bc095dc92e101734|000000db--ee9fe46e57--1"), # RIVIAN.RIVIAN_R1_GEN1 + ("TESLA", "2c912ca5de3b1ee9|0000025d--6eb6bcbca4--4"), # TESLA.TESLA_MODEL_Y # Enable when port is tested and dashcamOnly is no longer set #("VOLKSWAGEN2", "3cfdec54aa035f3f|2022-07-19--23-45-10--2"), # VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS @@ -58,6 +59,7 @@ segments = [ ("MAZDA", "regenACF84CCF482|2024-08-30--03-21-55--0"), ("FORD", "regen755D8CB1E1F|2025-04-08--23-13-43--0"), ("RIVIAN", "regen5FCAC896BBE|2025-04-08--23-13-35--0"), + ("TESLA", "2c912ca5de3b1ee9|0000025d--6eb6bcbca4--4"), ] # dashcamOnly makes don't need to be tested until a full port is done @@ -195,7 +197,7 @@ if __name__ == "__main__": continue # to speed things up, we only test all segments on card - if cfg.proc_name != 'card' and car_brand not in ('HYUNDAI', 'TOYOTA', 'HONDA', 'SUBARU', 'FORD', 'RIVIAN'): + if cfg.proc_name != 'card' and car_brand not in ('HYUNDAI', 'TOYOTA', 'HONDA', 'SUBARU', 'FORD', 'RIVIAN', 'TESLA'): continue cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{cur_commit}.zst") From 15a567ffee778c749433325747a60d16dd6f2dd2 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Tue, 13 May 2025 13:03:31 +0200 Subject: [PATCH 197/477] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index b4773f96b3..7eb5dba3dc 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit b4773f96b38a56089b28bf70b8073a9ddce6d847 +Subproject commit 7eb5dba3dc9960373128244c10fac99bba91f630 From 896dabb1905213bb8192de5c0d9045ecc860380c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 13 May 2025 21:31:50 +0800 Subject: [PATCH 198/477] system/ui: add right-side text eliding to gui_label (#35201) add optional right-side text eliding to gui_label --- system/ui/lib/label.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/system/ui/lib/label.py b/system/ui/lib/label.py index ccfd89a2ec..5244c6baf2 100644 --- a/system/ui/lib/label.py +++ b/system/ui/lib/label.py @@ -10,13 +10,27 @@ def gui_label( color: rl.Color = DEFAULT_TEXT_COLOR, font_weight: FontWeight = FontWeight.NORMAL, alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_LEFT, - alignment_vertical: int = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE + alignment_vertical: int = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + elide_right: bool = True ): - # Set font based on the provided weight font = gui_app.font(font_weight) - - # Measure text size text_size = rl.measure_text_ex(font, text, font_size, 0) + display_text = text + + # Elide text to fit within the rectangle + if elide_right and text_size.x > rect.width: + ellipsis = "..." + left, right = 0, len(text) + while left < right: + mid = (left + right) // 2 + candidate = text[:mid] + ellipsis + candidate_size = rl.measure_text_ex(font, candidate, font_size, 0) + if candidate_size.x <= rect.width: + left = mid + 1 + else: + right = mid + display_text = text[: left - 1] + ellipsis if left > 0 else ellipsis + text_size = rl.measure_text_ex(font, display_text, font_size, 0) # Calculate horizontal position based on alignment text_x = rect.x + { @@ -33,7 +47,7 @@ def gui_label( }.get(alignment_vertical, 0) # Draw the text in the specified rectangle - rl.draw_text_ex(font, text, rl.Vector2(text_x, text_y), font_size, 0, color) + rl.draw_text_ex(font, display_text, rl.Vector2(text_x, text_y), font_size, 0, color) def gui_text_box( From 73ff9dc944f05941729fa979db32ddacd573e5df Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 13 May 2025 21:34:17 +0800 Subject: [PATCH 199/477] system/ui: add require_full_screen to WifiManagerUI (#35191) add require_full_screen to WifiManagerUI --- system/ui/updater.py | 3 ++- system/ui/widgets/network.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/system/ui/updater.py b/system/ui/updater.py index 42acaf5675..3ee02ce97c 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -110,7 +110,8 @@ class Updater: # Draw the Wi-Fi manager UI wifi_rect = rl.Rectangle(MARGIN + 50, MARGIN, gui_app.width - MARGIN * 2 - 100, gui_app.height - MARGIN * 2 - BUTTON_HEIGHT - 20) self.wifi_manager_ui.render(wifi_rect) - + if self.wifi_manager_ui.require_full_screen: + return back_button_rect = rl.Rectangle(MARGIN, gui_app.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) if gui_button(back_button_rect, "Back"): diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index d94a3facb1..d34ad44f55 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -84,6 +84,11 @@ class WifiManagerUI: case _: self._draw_network_list(rect) + @property + def require_full_screen(self) -> bool: + """Check if the WiFi UI requires exclusive full-screen rendering.""" + return isinstance(self.state, (StateNeedsAuth, StateShowForgetConfirm)) + def _draw_network_list(self, rect: rl.Rectangle): content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT) offset = self.scroll_panel.handle_scroll(rect, content_rect) From 480cc57da38358a54de2718c5f28d0d3722c4b39 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Tue, 13 May 2025 10:32:33 -0600 Subject: [PATCH 200/477] Pass args to op auth (#35202) --- tools/op.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/op.sh b/tools/op.sh index 93e774a2ac..a7ed964812 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -254,7 +254,7 @@ function op_setup() { function op_auth() { op_before_cmd - op_run_command tools/lib/auth.py + op_run_command tools/lib/auth.py "$@" } function op_activate_venv() { From 3d696c0f90df5985ac0ff88df2afef8c39512e7f Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 13 May 2025 13:34:54 -0700 Subject: [PATCH 201/477] jenkins: remove logs during setup --- selfdrive/test/setup_device_ci.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/selfdrive/test/setup_device_ci.sh b/selfdrive/test/setup_device_ci.sh index 69555b19f7..d717698514 100755 --- a/selfdrive/test/setup_device_ci.sh +++ b/selfdrive/test/setup_device_ci.sh @@ -18,6 +18,9 @@ if [ -z "$TEST_DIR" ]; then exit 1 fi +# prevent storage from filling up +rm -rf /data/media/0/realdata/* + rm -rf /data/safe_staging/ || true if [ -d /data/safe_staging/ ]; then sudo umount /data/safe_staging/merged/ || true From 313ede3d1ed9b646ff5e94a83aaafe91ba5b6029 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 13 May 2025 14:48:40 -0700 Subject: [PATCH 202/477] Update NDOG live (#35205) * no alt exp * live * fix * update res --- opendbc_repo | 2 +- selfdrive/car/card.py | 8 -------- selfdrive/selfdrived/selfdrived.py | 4 ++-- selfdrive/test/process_replay/process_replay.py | 7 ------- selfdrive/test/process_replay/ref_commit | 2 +- 5 files changed, 4 insertions(+), 19 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 95ee4edd17..a633eeb65c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 95ee4edd17ecc6700eac12f2074de6b5478b9477 +Subproject commit a633eeb65c0807746cd9cf5cb1bb1dc227779f3f diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 378543164c..502daaa015 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -17,7 +17,6 @@ from opendbc.car.carlog import carlog from opendbc.car.fw_versions import ObdCallback from opendbc.car.car_helpers import get_car, interfaces from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase -from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp from openpilot.selfdrive.car.cruise import VCruiseHelper from openpilot.selfdrive.car.car_specific import MockCarState @@ -108,16 +107,9 @@ class Car: self.CI, self.CP = CI, CI.CP self.RI = RI - # set alternative experiences from parameters - disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.CP.alternativeExperience = 0 - if not disengage_on_accelerator: - self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS - openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle") - controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly - self.CP.passive = not controller_available or self.CP.dashcamOnly if self.CP.passive: safety_config = structs.CarParams.SafetyConfig() diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index c888551777..98c03acf1e 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -7,7 +7,6 @@ import cereal.messaging as messaging from cereal import car, log from msgq.visionipc import VisionIpcClient, VisionStreamType -from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.common.params import Params @@ -55,7 +54,6 @@ class SelfdriveD: self.CP = CP self.car_events = CarSpecificEvents(self.CP) - self.disengage_on_accelerator = not (self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS) # Setup sockets self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents']) @@ -85,6 +83,7 @@ class SelfdriveD: # read params self.is_metric = self.params.get_bool("IsMetric") self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") + self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") car_recognized = self.CP.brand != 'mock' @@ -486,6 +485,7 @@ class SelfdriveD: def params_thread(self, evt): while not evt.is_set(): self.is_metric = self.params.get_bool("IsMetric") + self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl self.personality = self.read_personality_param() time.sleep(0.1) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 3183db0c3a..8de2e60055 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -17,7 +17,6 @@ from cereal import car from cereal.services import SERVICE_LIST from msgq.visionipc import VisionIpcServer, get_endpoint_name as vipc_get_endpoint_name from opendbc.car.car_helpers import get_car, interfaces -from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.common.params import Params from openpilot.common.prefix import OpenpilotPrefix from openpilot.common.timeout import Timeout @@ -365,9 +364,6 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), cached_params=cached_params).CP - if not params.get_bool("DisengageOnAccelerator"): - CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS - params.put("CarParams", CP.to_bytes()) @@ -769,9 +765,6 @@ def generate_params_config(lr=None, CP=None, fingerprint=None, custom_params=Non params_dict["IsRhdDetected"] = is_rhd if CP is not None: - if CP.alternativeExperience == ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS: - params_dict["DisengageOnAccelerator"] = False - if fingerprint is None: if CP.fingerprintSource == "fw": params_dict["CarParamsCache"] = CP.as_builder().to_bytes() diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index f5cd05b16e..ab602d4726 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -b31b7c5c29e6d30ccee2fa5105af778810fcd02e \ No newline at end of file +61bfff810e45b7a1577cfbb572a7b3c233f8551b \ No newline at end of file From 67486ff92d9f78bcd14865a8bb81de0b94ded075 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 13 May 2025 16:59:35 -0700 Subject: [PATCH 203/477] bump tinygrad (#35208) * bump tinygrad * fix * why is mac different? * fix sim * relax that --- .gitmodules | 2 +- selfdrive/modeld/SConscript | 4 ++-- selfdrive/modeld/modeld.py | 5 +++-- selfdrive/test/test_onroad.py | 2 +- tinygrad_repo | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.gitmodules b/.gitmodules index 54c7393986..ad6530de9a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,4 +15,4 @@ url = ../../commaai/teleoprtc [submodule "tinygrad"] path = tinygrad_repo - url = https://github.com/commaai/tinygrad.git + url = https://github.com/tinygrad/tinygrad.git diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index cecebfa18b..0704dd4704 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -43,9 +43,9 @@ pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abs if arch == 'larch64': device_string = 'QCOM=1' elif arch == 'Darwin': - device_string = 'CLANG=1 IMAGE=0' + device_string = 'CLANG=1 IMAGE=0 JIT=2' else: - device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0' + device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0 JIT=2' for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: fn = File(f"models/{model_name}").abspath diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 5e1acdea0e..814bc7a999 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -1,13 +1,14 @@ #!/usr/bin/env python3 import os from openpilot.system.hardware import TICI -from tinygrad.tensor import Tensor -from tinygrad.dtype import dtypes if TICI: from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address os.environ['QCOM'] = '1' else: os.environ['LLVM'] = '1' + os.environ['JIT'] = '2' +from tinygrad.tensor import Tensor +from tinygrad.dtype import dtypes import time import pickle import numpy as np diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 4cd952219c..d0c725cacb 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -398,7 +398,7 @@ class TestOnroad: ("modelV2", 0.06, 0.040), # can miss cycles here and there, just important the avg frequency is 20Hz - ("driverStateV2", 0.2, 0.05), + ("driverStateV2", 0.3, 0.05), ] for (s, instant_max, avg_max) in cfgs: ts = [getattr(m, s).modelExecutionTime for m in self.msgs[s]] diff --git a/tinygrad_repo b/tinygrad_repo index 0e34f9082e..ddff9857b8 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 0e34f9082e9730b5df9c055b094a43e4565e413b +Subproject commit ddff9857b8527c16e5917eeec2dc55da69fa74d2 From 478015e7888424d255188e2a608e8d78ec95e740 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 13 May 2025 17:04:16 -0700 Subject: [PATCH 204/477] typo --- tools/scripts/setup_ssh_keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts/setup_ssh_keys.py b/tools/scripts/setup_ssh_keys.py index 699765eee1..45dc0aa977 100755 --- a/tools/scripts/setup_ssh_keys.py +++ b/tools/scripts/setup_ssh_keys.py @@ -18,6 +18,6 @@ if __name__ == "__main__": params.put_bool("SshEnabled", True) params.put("GithubSshKeys", keys.text) params.put("GithubUsername", username) - print("Setup ssh keys successfully") + print("Set up ssh keys successfully") else: print("Error getting public keys from github") From d0bf2be6f04e5035a21f1b5fa1a7f4d2f0217f8b Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 13 May 2025 17:12:32 -0700 Subject: [PATCH 205/477] External GPU support for big models (#35172) * usb gpu * cleanup --------- Co-authored-by: Comma Device --- selfdrive/modeld/SConscript | 19 ++++++++++--------- selfdrive/modeld/modeld.py | 12 +++++++++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 0704dd4704..28d60b9e00 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -1,3 +1,4 @@ +import os import glob Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'transformations') @@ -13,7 +14,6 @@ common_src = [ "transforms/transform.cc", ] - # OpenCL is a framework on Mac if arch == "Darwin": frameworks += ['OpenCL'] @@ -40,15 +40,16 @@ for model_name in ['driving_vision', 'driving_policy']: # Compile tinygrad model pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"' -if arch == 'larch64': - device_string = 'QCOM=1' -elif arch == 'Darwin': - device_string = 'CLANG=1 IMAGE=0 JIT=2' -else: - device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0 JIT=2' - for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: + if "USBGPU" in os.environ and not model_name.startswith("dmon"): + device_string = "AMD=1 AMD_LLVM=1 NOLOCALS=0 IMAGE=0" + elif arch == 'larch64': + device_string = 'QCOM=1' + elif arch == 'Darwin': + device_string = 'CLANG=1 IMAGE=0 JIT=2' + else: + device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0 JIT=2' + fn = File(f"models/{model_name}").abspath cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd) - diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 814bc7a999..cdb16a6f63 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 import os from openpilot.system.hardware import TICI -if TICI: +USBGPU = "USBGPU" in os.environ +if USBGPU: + os.environ['AMD'] = '1' +elif TICI: from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address os.environ['QCOM'] = '1' else: @@ -147,7 +150,7 @@ class ModelState: imgs_cl = {'input_imgs': self.frames['input_imgs'].prepare(buf, transform.flatten()), 'big_input_imgs': self.frames['big_input_imgs'].prepare(wbuf, transform_wide.flatten())} - if TICI: + if TICI and not USBGPU: # The imgs tensors are backed by opencl memory, only need init once for key in imgs_cl: if key not in self.vision_inputs: @@ -188,7 +191,10 @@ def main(demo=False): sentry.set_tag("daemon", PROCESS_NAME) cloudlog.bind(daemon=PROCESS_NAME) setproctitle(PROCESS_NAME) - config_realtime_process(7, 54) + if not USBGPU: + # USB GPU currently saturates a core so can't do this yet, + # also need to move the aux USB interrupts for good timings + config_realtime_process(7, 54) cloudlog.warning("setting up CL context") cl_context = CLContext() From 9d7fce8dd1e29a4e8e80eee843b05d4ba7411be9 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 13 May 2025 18:48:21 -0700 Subject: [PATCH 206/477] CI: bump tinygrad in cronjob --- .github/workflows/repo-maintenance.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/repo-maintenance.yaml b/.github/workflows/repo-maintenance.yaml index 3bcb143825..e041b07ec8 100644 --- a/.github/workflows/repo-maintenance.yaml +++ b/.github/workflows/repo-maintenance.yaml @@ -50,7 +50,7 @@ jobs: - name: bump submodules run: | git config --global --add safe.directory '*' - git -c submodule."tinygrad".update=none submodule update --remote + git submodule update --remote git add . - name: update car docs run: | From 77c35f8e5dd9bc952b63a754eb9ed810983094c7 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Tue, 13 May 2025 18:55:58 -0700 Subject: [PATCH 207/477] [bot] Update Python packages (#35214) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- panda | 2 +- tinygrad_repo | 2 +- uv.lock | 39 +++++++++++++++++++++------------------ 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index a633eeb65c..7faa3a0811 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit a633eeb65c0807746cd9cf5cb1bb1dc227779f3f +Subproject commit 7faa3a0811ddc7bf7ba38f1c8e2d1b6c17342a30 diff --git a/panda b/panda index 7eb5dba3dc..6adac802b5 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 7eb5dba3dc9960373128244c10fac99bba91f630 +Subproject commit 6adac802b5dce739e35b734b81b8d20fca4bf700 diff --git a/tinygrad_repo b/tinygrad_repo index ddff9857b8..f726f79a9e 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit ddff9857b8527c16e5917eeec2dc55da69fa74d2 +Subproject commit f726f79a9e1b3ab6dafb3dc012feb7901afdf721 diff --git a/uv.lock b/uv.lock index 60e0e521c5..5eafe7b636 100644 --- a/uv.lock +++ b/uv.lock @@ -163,7 +163,7 @@ wheels = [ [[package]] name = "azure-identity" -version = "1.22.0" +version = "1.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -172,9 +172,9 @@ dependencies = [ { name = "msal-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/8e/1b5916f5e1696bf05b009cf7d41383cea54aa8536d4a4f6f88cca15eb6a4/azure_identity-1.22.0.tar.gz", hash = "sha256:c8f5ef23e5295c2fa300c984dd9f5e1fe43503fc25c121c37ff6a15e39b800b9", size = 263346, upload-time = "2025-05-06T20:22:24.13Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/52/458c1be17a5d3796570ae2ed3c6b7b55b134b22d5ef8132b4f97046a9051/azure_identity-1.23.0.tar.gz", hash = "sha256:d9cdcad39adb49d4bb2953a217f62aec1f65bbb3c63c9076da2be2a47e53dde4", size = 265280, upload-time = "2025-05-14T00:18:30.408Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/1a/6f13d7f95f68f37303c0e00e011d498e4524e70d354b2e11ef5ae89e0ce0/azure_identity-1.22.0-py3-none-any.whl", hash = "sha256:26d6c63f2ca453c77c3e74be8613941ad074e05d0c8be135247573752c249ad8", size = 185524, upload-time = "2025-05-06T20:22:25.991Z" }, + { url = "https://files.pythonhosted.org/packages/07/16/a51d47780f41e4b87bb2d454df6aea90a44a346e918ac189d3700f3d728d/azure_identity-1.23.0-py3-none-any.whl", hash = "sha256:dbbeb64b8e5eaa81c44c565f264b519ff2de7ff0e02271c49f3cb492762a50b0", size = 186097, upload-time = "2025-05-14T00:18:32.734Z" }, ] [[package]] @@ -1217,24 +1217,27 @@ wheels = [ [[package]] name = "onnx" -version = "1.17.0" +version = "1.18.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "protobuf" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/54/0e385c26bf230d223810a9c7d06628d954008a5e5e4b73ee26ef02327282/onnx-1.17.0.tar.gz", hash = "sha256:48ca1a91ff73c1d5e3ea2eef20ae5d0e709bb8a2355ed798ffc2169753013fd3", size = 12165120, upload-time = "2024-10-01T21:48:40.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/60/e56e8ec44ed34006e6d4a73c92a04d9eea6163cc12440e35045aec069175/onnx-1.18.0.tar.gz", hash = "sha256:3d8dbf9e996629131ba3aa1afd1d8239b660d1f830c6688dd7e03157cccd6b9c", size = 12563009, upload-time = "2025-05-12T22:03:09.626Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/a9/8d1b1d53aec70df53e0f57e9f9fcf47004276539e29230c3d5f1f50719ba/onnx-1.17.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:d6fc3a03fc0129b8b6ac03f03bc894431ffd77c7d79ec023d0afd667b4d35869", size = 16647991, upload-time = "2024-10-01T21:46:02.491Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e3/cc80110e5996ca61878f7b4c73c7a286cd88918ff35eacb60dc75ab11ef5/onnx-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a4b63d4e1d8ec3e2f069e7b798b2955810aa434f7361f01bc8ca08d69cce4", size = 15908949, upload-time = "2024-10-01T21:46:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/b1/2f/91092557ed478e323a2b4471e2081fdf88d1dd52ae988ceaf7db4e4506ff/onnx-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a183c6178be001bf398260e5ac2c927dc43e7746e8638d6c05c20e321f8c949", size = 16048190, upload-time = "2024-10-01T21:46:08.041Z" }, - { url = "https://files.pythonhosted.org/packages/ac/59/9ea23fc22d0bb853133f363e6248e31bcbc6c1c90543a3938c00412ac02a/onnx-1.17.0-cp311-cp311-win32.whl", hash = "sha256:081ec43a8b950171767d99075b6b92553901fa429d4bc5eb3ad66b36ef5dbe3a", size = 14424299, upload-time = "2024-10-01T21:46:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/51/a5/19b0dfcb567b62e7adf1a21b08b23224f0c2d13842aee4d0abc6f07f9cf5/onnx-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:95c03e38671785036bb704c30cd2e150825f6ab4763df3a4f1d249da48525957", size = 14529142, upload-time = "2024-10-01T21:46:12.574Z" }, - { url = "https://files.pythonhosted.org/packages/b4/dd/c416a11a28847fafb0db1bf43381979a0f522eb9107b831058fde012dd56/onnx-1.17.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:0e906e6a83437de05f8139ea7eaf366bf287f44ae5cc44b2850a30e296421f2f", size = 16651271, upload-time = "2024-10-01T21:46:16.084Z" }, - { url = "https://files.pythonhosted.org/packages/f0/6c/f040652277f514ecd81b7251841f96caa5538365af7df07f86c6018cda2b/onnx-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d955ba2939878a520a97614bcf2e79c1df71b29203e8ced478fa78c9a9c63c2", size = 15907522, upload-time = "2024-10-01T21:46:18.574Z" }, - { url = "https://files.pythonhosted.org/packages/3d/7c/67f4952d1b56b3f74a154b97d0dd0630d525923b354db117d04823b8b49b/onnx-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3fb5cc4e2898ac5312a7dc03a65133dd2abf9a5e520e69afb880a7251ec97a", size = 16046307, upload-time = "2024-10-01T21:46:21.186Z" }, - { url = "https://files.pythonhosted.org/packages/ae/20/6da11042d2ab870dfb4ce4a6b52354d7651b6b4112038b6d2229ab9904c4/onnx-1.17.0-cp312-cp312-win32.whl", hash = "sha256:317870fca3349d19325a4b7d1b5628f6de3811e9710b1e3665c68b073d0e68d7", size = 14424235, upload-time = "2024-10-01T21:46:24.343Z" }, - { url = "https://files.pythonhosted.org/packages/35/55/c4d11bee1fdb0c4bd84b4e3562ff811a19b63266816870ae1f95567aa6e1/onnx-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:659b8232d627a5460d74fd3c96947ae83db6d03f035ac633e20cd69cfa029227", size = 14530453, upload-time = "2024-10-01T21:46:26.981Z" }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a336dac4db1eddba2bf577191e5b7d3e4c26fcee5ec518a5a5b11d13540d/onnx-1.18.0-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:735e06d8d0cf250dc498f54038831401063c655a8d6e5975b2527a4e7d24be3e", size = 18281831, upload-time = "2025-05-12T22:02:06.429Z" }, + { url = "https://files.pythonhosted.org/packages/02/3a/56475a111120d1e5d11939acbcbb17c92198c8e64a205cd68e00bdfd8a1f/onnx-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73160799472e1a86083f786fecdf864cf43d55325492a9b5a1cfa64d8a523ecc", size = 17424359, upload-time = "2025-05-12T22:02:09.866Z" }, + { url = "https://files.pythonhosted.org/packages/cf/03/5eb5e9ef446ed9e78c4627faf3c1bc25e0f707116dd00e9811de232a8df5/onnx-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6acafb3823238bbe8f4340c7ac32fb218689442e074d797bee1c5c9a02fdae75", size = 17586006, upload-time = "2025-05-12T22:02:13.217Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4e/70943125729ce453271a6e46bb847b4a612496f64db6cbc6cb1f49f41ce1/onnx-1.18.0-cp311-cp311-win32.whl", hash = "sha256:4c8c4bbda760c654e65eaffddb1a7de71ec02e60092d33f9000521f897c99be9", size = 15734988, upload-time = "2025-05-12T22:02:16.561Z" }, + { url = "https://files.pythonhosted.org/packages/44/b0/435fd764011911e8f599e3361f0f33425b1004662c1ea33a0ad22e43db2d/onnx-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5810194f0f6be2e58c8d6dedc6119510df7a14280dd07ed5f0f0a85bd74816a", size = 15849576, upload-time = "2025-05-12T22:02:19.569Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f0/9e31f4b4626d60f1c034f71b411810bc9fafe31f4e7dd3598effd1b50e05/onnx-1.18.0-cp311-cp311-win_arm64.whl", hash = "sha256:aa1b7483fac6cdec26922174fc4433f8f5c2f239b1133c5625063bb3b35957d0", size = 15822961, upload-time = "2025-05-12T22:02:22.735Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fe/16228aca685392a7114625b89aae98b2dc4058a47f0f467a376745efe8d0/onnx-1.18.0-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:521bac578448667cbb37c50bf05b53c301243ede8233029555239930996a625b", size = 18285770, upload-time = "2025-05-12T22:02:26.116Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/ba50a903a9b5e6f9be0fa50f59eb2fca4a26ee653375408fbc72c3acbf9f/onnx-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4da451bf1c5ae381f32d430004a89f0405bc57a8471b0bddb6325a5b334aa40", size = 17421291, upload-time = "2025-05-12T22:02:29.645Z" }, + { url = "https://files.pythonhosted.org/packages/11/23/25ec2ba723ac62b99e8fed6d7b59094dadb15e38d4c007331cc9ae3dfa5f/onnx-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99afac90b4cdb1471432203c3c1f74e16549c526df27056d39f41a9a47cfb4af", size = 17584084, upload-time = "2025-05-12T22:02:32.789Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4d/2c253a36070fb43f340ff1d2c450df6a9ef50b938adcd105693fee43c4ee/onnx-1.18.0-cp312-cp312-win32.whl", hash = "sha256:ee159b41a3ae58d9c7341cf432fc74b96aaf50bd7bb1160029f657b40dc69715", size = 15734892, upload-time = "2025-05-12T22:02:35.527Z" }, + { url = "https://files.pythonhosted.org/packages/e8/92/048ba8fafe6b2b9a268ec2fb80def7e66c0b32ab2cae74de886981f05a27/onnx-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:102c04edc76b16e9dfeda5a64c1fccd7d3d2913b1544750c01d38f1ac3c04e05", size = 15850336, upload-time = "2025-05-12T22:02:38.545Z" }, + { url = "https://files.pythonhosted.org/packages/a1/66/bbc4ffedd44165dcc407a51ea4c592802a5391ce3dc94aa5045350f64635/onnx-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:911b37d724a5d97396f3c2ef9ea25361c55cbc9aa18d75b12a52b620b67145af", size = 15823802, upload-time = "2025-05-12T22:02:42.037Z" }, ] [[package]] @@ -4586,14 +4589,14 @@ wheels = [ [[package]] name = "pyyaml-env-tag" -version = "1.0" +version = "1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/95/32c8c79d784552ed687c676924381c0dc88b2a0248b50a32f4b5ac0ba03c/pyyaml_env_tag-1.0.tar.gz", hash = "sha256:bc952534a872b583f66f916e2dd83e7a7b9087847f4afca6d9c957c48b258ed2", size = 4462, upload-time = "2025-05-09T18:09:14.1Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/8c/c35fdb193c3717bdb4dea0ea361dbe81997164e01deaa2809cc2d71aa6b6/pyyaml_env_tag-1.0-py3-none-any.whl", hash = "sha256:37f081041b8dca44ed8eb931ce0056f97de17251450f0ed08773dc2bcaf9e683", size = 4681, upload-time = "2025-05-09T18:09:12.611Z" }, + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, ] [[package]] From 98c4a4582bf666c5ae5495301d603dae88a1d62f Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Tue, 13 May 2025 18:56:13 -0700 Subject: [PATCH 208/477] chore: reformat clip params for extension (#35212) --- tools/clip/run.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 5fa3c0ae69..9414f8fba7 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -156,7 +156,17 @@ def wait_for_frames(procs: list[Popen]): check_for_failure(proc) -def clip(data_dir: str | None, quality: Literal['low', 'high'], prefix: str, route: Route, out: str, start: int, end: int, target_mb: int, title: str | None): +def clip( + data_dir: str | None, + quality: Literal['low', 'high'], + prefix: str, + route: Route, + out: str, + start: int, + end: int, + target_mb: int, + title: str | None, +): logger.info(f'clipping route {route.name.canonical_name}, start={start} end={end} quality={quality} target_filesize={target_mb}MB') begin_at = max(start - SECONDS_TO_WARM, 0) @@ -256,7 +266,17 @@ def main(): p.add_argument('-t', '--title', help='overlay this title on the video (e.g. "Chill driving across the Golden Gate Bridge")', type=validate_title) args = parse_args(p) try: - clip(args.data_dir, args.quality, args.prefix, args.route, args.output, args.start, args.end, args.file_size, args.title) + clip( + data_dir=args.data_dir, + quality=args.quality, + prefix=args.prefix, + route=args.route, + out=args.output, + start=args.start, + end=args.end, + target_mb=args.file_size, + title=args.title, + ) except KeyboardInterrupt as e: logger.exception('interrupted by user', exc_info=e) except Exception as e: From 1a3e3423035112b287a8fd0f73ef4222e4dd58ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Wed, 14 May 2025 04:22:44 +0200 Subject: [PATCH 209/477] ui: reset live parameters and delay when reset calibration pressed (#35211) * Reset params and lag too * Old param too --- selfdrive/ui/qt/offroad/settings.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 7a79413e2d..2057f1223e 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -194,6 +194,9 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) { params.remove("CalibrationParams"); params.remove("LiveTorqueParameters"); + params.remove("LiveParameters"); + params.remove("LiveParametersV2"); + params.remove("LiveDelay"); } }); addItem(resetCalibBtn); @@ -326,7 +329,7 @@ void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { if (param.endsWith("Panel")) { QString panelName = param; panelName.chop(5); // Remove "Panel" suffix - + // Find the panel by name for (int i = 0; i < nav_btns->buttons().size(); i++) { if (nav_btns->buttons()[i]->text() == tr(panelName.toStdString().c_str())) { @@ -338,7 +341,7 @@ void SettingsWindow::setCurrentPanel(int index, const QString ¶m) { emit expandToggleDescription(param); } } - + panel_widget->setCurrentIndex(index); nav_btns->buttons()[index]->setChecked(true); } From bdffd3408855b167e394199d4550d48aa566c225 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Tue, 13 May 2025 19:23:36 -0700 Subject: [PATCH 210/477] feat(clip): support metric units if requested (#35213) --- tools/clip/run.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/clip/run.py b/tools/clip/run.py index 9414f8fba7..d6b75566b1 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -15,6 +15,7 @@ from typing import Literal from cereal.messaging import SubMaster from openpilot.common.basedir import BASEDIR +from openpilot.common.params import Params from openpilot.common.prefix import OpenpilotPrefix from openpilot.tools.lib.route import Route @@ -166,6 +167,7 @@ def clip( end: int, target_mb: int, title: str | None, + use_metric: bool = False, ): logger.info(f'clipping route {route.name.canonical_name}, start={start} end={end} quality={quality} target_filesize={target_mb}MB') @@ -225,8 +227,11 @@ def clip( xvfb_proc = start_proc(xvfb_cmd, env) atexit.register(lambda: xvfb_proc.terminate()) + ui_proc = start_proc(ui_cmd, env) atexit.register(lambda: ui_proc.terminate()) + Params().put('IsMetric', '1' if use_metric else '0') + replay_proc = start_proc(replay_cmd, env) atexit.register(lambda: replay_proc.terminate()) procs = [replay_proc, ui_proc, xvfb_proc] @@ -263,6 +268,7 @@ def main(): p.add_argument('-p', '--prefix', help='openpilot prefix', default=f'clip_{randint(100, 99999)}') p.add_argument('-q', '--quality', help='quality of camera (low = qcam, high = hevc)', choices=['low', 'high'], default='high') p.add_argument('-s', '--start', help='start clipping at seconds', type=int) + p.add_argument('-m', '--metric', help='use metric units in ui (e.g. kph)', action='store_true') p.add_argument('-t', '--title', help='overlay this title on the video (e.g. "Chill driving across the Golden Gate Bridge")', type=validate_title) args = parse_args(p) try: @@ -276,6 +282,7 @@ def main(): end=args.end, target_mb=args.file_size, title=args.title, + use_metric=args.metric, ) except KeyboardInterrupt as e: logger.exception('interrupted by user', exc_info=e) From cbec1886e7e17e5ab4d8ab80409c39492ac21917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Wed, 14 May 2025 04:26:28 +0200 Subject: [PATCH 211/477] paramsd: remove saved param if mismatch (#35215) remove LiveParametersV2 --- selfdrive/locationd/paramsd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index a7712ba1d9..ec15f501ae 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -248,6 +248,7 @@ def retrieve_initial_vehicle_params(params: Params, CP: car.CarParams, replay: b retrieve_success = True except Exception as e: cloudlog.error(f"Failed to retrieve initial values: {e}") + params.remove("LiveParametersV2") if not replay: # When driving in wet conditions the stiffness can go down, and then be too low on the next drive From f242b1e88a703d7f6f3ab53f4534e7c684d6ebe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Wed, 14 May 2025 05:18:14 +0200 Subject: [PATCH 212/477] lagd: remove saved param if mismatch (#35217) Remove LiveDelay on mismatch --- selfdrive/locationd/lagd.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index f4f46b7469..b4f4e1f663 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -316,9 +316,9 @@ class LateralLagEstimator: return lag, corr -def retrieve_initial_lag(params_reader: Params, CP: car.CarParams): - last_lag_data = params_reader.get("LiveDelay") - last_carparams_data = params_reader.get("CarParamsPrevRoute") +def retrieve_initial_lag(params: Params, CP: car.CarParams): + last_lag_data = params.get("LiveDelay") + last_carparams_data = params.get("CarParamsPrevRoute") if last_lag_data is not None: try: @@ -333,6 +333,7 @@ def retrieve_initial_lag(params_reader: Params, CP: car.CarParams): return lag, valid_blocks except Exception as e: cloudlog.error(f"Failed to retrieve initial lag: {e}") + params.remove("LiveDelay") return None @@ -345,11 +346,11 @@ def main(): pm = messaging.PubMaster(['liveDelay']) sm = messaging.SubMaster(['livePose', 'liveCalibration', 'carState', 'controlsState', 'carControl'], poll='livePose') - params_reader = Params() - CP = messaging.log_from_bytes(params_reader.get("CarParams", block=True), car.CarParams) + params = Params() + CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) lag_learner = LateralLagEstimator(CP, 1. / SERVICE_LIST['livePose'].frequency) - if (initial_lag_params := retrieve_initial_lag(params_reader, CP)) is not None: + if (initial_lag_params := retrieve_initial_lag(params, CP)) is not None: lag, valid_blocks = initial_lag_params lag_learner.reset(lag, valid_blocks) @@ -370,4 +371,4 @@ def main(): pm.send('liveDelay', lag_msg_dat) if sm.frame % 1200 == 0: # cache every 60 seconds - params_reader.put_nonblocking("LiveDelay", lag_msg_dat) + params.put_nonblocking("LiveDelay", lag_msg_dat) From 3fd05d2aceb2fc52c96efce58ae3e7c44d882c4a Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 13 May 2025 21:05:17 -0700 Subject: [PATCH 213/477] more robust modem --- system/hardware/tici/hardware.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 694d42f4d6..5a8e41b51f 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -297,13 +297,11 @@ class Tici(HardwareBase): return None def get_modem_temperatures(self): - if self.get_device_type() == "mici": - return [] timeout = 0.2 # Default timeout is too short try: modem = self.get_modem() temps = modem.Command("AT+QTEMP", math.ceil(timeout), dbus_interface=MM_MODEM, timeout=timeout) - return list(map(int, temps.split(' ')[1].split(','))) + return list(filter(lambda t: t != 255, map(int, temps.split(' ')[1].split(',')))) except Exception: return [] From 997a99926cba018a48a513bf0ae8d834d7749145 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 13 May 2025 21:06:30 -0700 Subject: [PATCH 214/477] ci: overwrite previous comment for model_replay (#35209) * less * test * fix * cleanup --- selfdrive/test/process_replay/model_replay.py | 2 +- tools/lib/github_utils.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py index af626e2da9..0578a61588 100755 --- a/selfdrive/test/process_replay/model_replay.py +++ b/selfdrive/test/process_replay/model_replay.py @@ -123,7 +123,7 @@ def comment_replay_report(proposed, master, full_logs): diff_plots = create_table("Model Replay Differences", diff_files, link, open_table=True) all_plots = create_table("All Model Replay Plots", files, link) comment = f"ref for commit {commit}: {link}/{log_name}" + diff_plots + all_plots - GITHUB.comment_on_pr(comment, PR_BRANCH) + GITHUB.comment_on_pr(comment, PR_BRANCH, "commaci-public", True) def trim_logs_to_max_frames(logs, max_frames, frs_types, include_all_types): all_msgs = [] diff --git a/tools/lib/github_utils.py b/tools/lib/github_utils.py index 4dc22b9524..46a0dcf3cb 100644 --- a/tools/lib/github_utils.py +++ b/tools/lib/github_utils.py @@ -87,8 +87,8 @@ class GithubUtils: def comment_on_pr(self, comment, pr_branch, commenter="", overwrite=False): pr_number = self.get_pr_number(pr_branch) data = f'{{"body": "{comment}"}}' + github_path = f'issues/{pr_number}/comments' if overwrite: - github_path = f'issues/{pr_number}/comments' r = self.api_call(github_path) comments = [x['id'] for x in r.json() if x['user']['login'] == commenter] if comments: @@ -96,7 +96,6 @@ class GithubUtils: self.api_call(github_path, data=data, method=HTTPMethod.PATCH) return - github_path=f'issues/{pr_number}/comments' self.api_call(github_path, data=data, method=HTTPMethod.POST) # upload files to github and comment them on the pr From 92e27f0ba29eb254e7ab2c955aa2f2ae71044287 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 14 May 2025 14:34:11 +0100 Subject: [PATCH 215/477] fix .editorconfig --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 879e6eebca..d506433ece 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,7 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true -[{*.py, *.pyx, *.pxd}] +[*.{py,pyx,pxd}] charset = utf-8 indent_style = space indent_size = 2 From c57f576436083e61ee134865aca3623146a28e2a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 14 May 2025 21:49:47 +0800 Subject: [PATCH 216/477] system/ui: fix FPS drop issues (#35220) set VSYNC flag only when the env is set --- system/ui/lib/application.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index c359b1e578..b9d4293738 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -12,6 +12,7 @@ FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions +ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1' @@ -52,9 +53,13 @@ class GuiApplication: HARDWARE.set_screen_brightness(65) self._set_log_callback() - rl.set_trace_log_level(rl.TraceLogLevel.LOG_ALL) - rl.set_config_flags(rl.ConfigFlags.FLAG_MSAA_4X_HINT | rl.ConfigFlags.FLAG_VSYNC_HINT) + + flags = rl.ConfigFlags.FLAG_MSAA_4X_HINT + if ENABLE_VSYNC: + flags |= rl.ConfigFlags.FLAG_VSYNC_HINT + rl.set_config_flags(flags) + rl.init_window(self._width, self._height, title) rl.set_target_fps(fps) From b1bdab0c9084632d930eeef0de6307ef3cb3f458 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 14 May 2025 23:11:38 +0800 Subject: [PATCH 217/477] system/ui: improve texture caching (#35222) improve texture caching --- system/ui/lib/application.py | 50 ++++++++++++++++++++++++++++++------ system/ui/spinner.py | 7 ++--- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index b9d4293738..ba1441170b 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -18,6 +18,7 @@ STRICT_MODE = os.getenv("STRICT_MODE") == '1' DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.Color(200, 200, 200, 255) +ASSETS_DIR = os.path.join(BASEDIR, "selfdrive/assets") FONT_DIR = os.path.join(BASEDIR, "selfdrive/assets/fonts") @@ -37,7 +38,7 @@ class GuiApplication: self._fonts: dict[FontWeight, rl.Font] = {} self._width = width self._height = height - self._textures: list[rl.Texture] = [] + self._textures: dict[str, rl.Texture] = {} self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() self._window_close_requested = False @@ -67,28 +68,61 @@ class GuiApplication: self._set_styles() self._load_fonts() - def load_texture_from_image(self, file_name: str, width: int, height: int, alpha_premultiply = False): + + def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): + cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" + if cache_key in self._textures: + return self._textures[cache_key] + + texture_obj = self._load_texture_from_image(os.path.join(ASSETS_DIR, asset_path), width, height, alpha_premultiply, keep_aspect_ratio) + self._textures[cache_key] = texture_obj + return texture_obj + + def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): """Load and resize a texture, storing it for later automatic unloading.""" - image = rl.load_image(file_name) + if image_path.endswith('.svg'): + image = self._load_image_from_svg(image_path) + else: + image = rl.load_image(image_path) + if alpha_premultiply: rl.image_alpha_premultiply(image) - rl.image_resize(image, width, height) + + # Resize with aspect ratio preservation if requested + if keep_aspect_ratio: + orig_width = image.width + orig_height = image.height + + scale_width = width / orig_width + scale_height = height / orig_height + + # Calculate new dimensions + scale = min(scale_width, scale_height) + new_width = int(orig_width * scale) + new_height = int(orig_height * scale) + + rl.image_resize(image, new_width, new_height) + else: + rl.image_resize(image, width, height) + texture = rl.load_texture_from_image(image) # Set texture filtering to smooth the result rl.set_texture_filter(texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) rl.unload_image(image) - - self._textures.append(texture) return texture + def _load_image_from_svg(self, svg_path: str): + # TODO: Implement SVG loading + assert(0) + def close(self): if not rl.is_window_ready(): return - for texture in self._textures: + for texture in self._textures.values(): rl.unload_texture(texture) - self._textures = [] + self._textures = {} for font in self._fonts.values(): rl.unload_font(font) diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 119bdba3e7..93bce3887e 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 import pyray as rl -import os import threading import time -from openpilot.common.basedir import BASEDIR from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.window import BaseWindow from openpilot.system.ui.text import wrap_text @@ -26,9 +24,8 @@ def clamp(value, min_value, max_value): class SpinnerRenderer: def __init__(self): - self._comma_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_comma.png"), TEXTURE_SIZE, TEXTURE_SIZE) - self._spinner_texture = gui_app.load_texture_from_image(os.path.join(BASEDIR, "selfdrive/assets/img_spinner_track.png"), TEXTURE_SIZE, TEXTURE_SIZE, - alpha_premultiply=True) + self._comma_texture = gui_app.texture("img_spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE) + self._spinner_texture = gui_app.texture("img_spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True) self._rotation = 0.0 self._progress: int | None = None self._wrapped_lines: list[str] = [] From 665d2c3bec3bd4da393e788ac46e5f6719608d7b Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 14 May 2025 08:14:44 -0700 Subject: [PATCH 218/477] feat(clip): hydrate route's CarParams before starting UI (#35218) * feat: hydrate CarParams before starting UI * only get first segment, faster --- tools/clip/run.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index d6b75566b1..87a5d79d2e 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -18,6 +18,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params from openpilot.common.prefix import OpenpilotPrefix from openpilot.tools.lib.route import Route +from openpilot.tools.lib.logreader import LogReader, ReadMode, comma_api_source DEFAULT_OUTPUT = 'output.mp4' DEMO_START = 90 @@ -114,6 +115,20 @@ def parse_args(parser: ArgumentParser): return args +def populate_car_params(route: Route): + segment = route.name.canonical_name + '/0' # only get first segment qlog + lr = LogReader(segment, default_mode=ReadMode.QLOG, source=comma_api_source) + init_data = lr.first('initData') + assert init_data is not None + + params = Params() + entries = init_data.params.entries + for cp in entries: + key, value = cp.key, cp.value + params.put(key, value) + logger.info(f'persisted {len(entries)} CarParam(s)') + + def start_proc(args: list[str], env: dict[str, str]): return Popen(args, env=env, stdout=PIPE, stderr=PIPE) @@ -167,7 +182,6 @@ def clip( end: int, target_mb: int, title: str | None, - use_metric: bool = False, ): logger.info(f'clipping route {route.name.canonical_name}, start={start} end={end} quality={quality} target_filesize={target_mb}MB') @@ -222,16 +236,15 @@ def clip( xvfb_cmd = ['Xvfb', display, '-terminate', '-screen', '0', f'{RESOLUTION}x{PIXEL_DEPTH}'] with OpenpilotPrefix(prefix, shared_download_cache=True): + populate_car_params(route) + env = os.environ.copy() env['DISPLAY'] = display xvfb_proc = start_proc(xvfb_cmd, env) atexit.register(lambda: xvfb_proc.terminate()) - ui_proc = start_proc(ui_cmd, env) atexit.register(lambda: ui_proc.terminate()) - Params().put('IsMetric', '1' if use_metric else '0') - replay_proc = start_proc(replay_cmd, env) atexit.register(lambda: replay_proc.terminate()) procs = [replay_proc, ui_proc, xvfb_proc] @@ -268,7 +281,6 @@ def main(): p.add_argument('-p', '--prefix', help='openpilot prefix', default=f'clip_{randint(100, 99999)}') p.add_argument('-q', '--quality', help='quality of camera (low = qcam, high = hevc)', choices=['low', 'high'], default='high') p.add_argument('-s', '--start', help='start clipping at seconds', type=int) - p.add_argument('-m', '--metric', help='use metric units in ui (e.g. kph)', action='store_true') p.add_argument('-t', '--title', help='overlay this title on the video (e.g. "Chill driving across the Golden Gate Bridge")', type=validate_title) args = parse_args(p) try: @@ -282,7 +294,6 @@ def main(): end=args.end, target_mb=args.file_size, title=args.title, - use_metric=args.metric, ) except KeyboardInterrupt as e: logger.exception('interrupted by user', exc_info=e) From 64b0ede9ae2a02b0e8d4aa736f73e09e1f54c540 Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Wed, 14 May 2025 20:17:50 +0200 Subject: [PATCH 219/477] fix: typos (#35224) * fix: typos * Update what-is-a-car-port.md --------- Co-authored-by: Adeeb Shihadeh --- docs/contributing/roadmap.md | 2 +- selfdrive/pandad/pandad.cc | 2 +- selfdrive/test/process_replay/README.md | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/contributing/roadmap.md b/docs/contributing/roadmap.md index 7086d85b85..1262017a0b 100644 --- a/docs/contributing/roadmap.md +++ b/docs/contributing/roadmap.md @@ -12,7 +12,7 @@ This is the roadmap for the next major openpilot releases. Also check out openpilot 0.10 will be the first release with a driving policy trained in a [learned simulator](https://youtu.be/EqQNZXqzFSI). -* Driving model trained in a learned simlator +* Driving model trained in a learned simulator * Always-on driver monitoring (behind a toggle) * GPS removed from the driving stack * 100KB qlogs diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index fbf4e206ae..1666c6229f 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -25,7 +25,7 @@ // - If a panda connection is dropped, pandad will reconnect to all pandas // - If a panda is added, we will only reconnect when we are offroad // CAN buses: -// - Each panda will have it's block of 4 buses. E.g.: the second panda will use +// - Each panda will have its block of 4 buses. E.g.: the second panda will use // bus numbers 4, 5, 6 and 7 // - The internal panda will always be used for accessing the OBD2 port, // and thus firmware queries diff --git a/selfdrive/test/process_replay/README.md b/selfdrive/test/process_replay/README.md index 381e4dcb7f..dc801e4285 100644 --- a/selfdrive/test/process_replay/README.md +++ b/selfdrive/test/process_replay/README.md @@ -38,7 +38,7 @@ optional arguments: ## Forks -openpilot forks can use this test with their own reference logs, by default `test_proccess.py` saves logs locally. +openpilot forks can use this test with their own reference logs, by default `test_proccesses.py` saves logs locally. To generate new logs: @@ -48,13 +48,13 @@ Then, check in the new logs using git-lfs. Make sure to also update the `ref_com ## API -Process replay test suite exposes programmatic APIs for simultaneously running processes or groups of processes on provided logs. +Process replay test suite exposes programmatic APIs for simultaneously running processes or groups of processes on provided logs. ```py def replay_process_with_name(name: Union[str, Iterable[str]], lr: LogIterable, *args, **kwargs) -> List[capnp._DynamicStructReader]: def replay_process( - cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: LogIterable, frs: Optional[Dict[str, Any]] = None, + cfg: Union[ProcessConfig, Iterable[ProcessConfig]], lr: LogIterable, frs: Optional[Dict[str, Any]] = None, fingerprint: Optional[str] = None, return_all_logs: bool = False, custom_params: Optional[Dict[str, Any]] = None, disable_progress: bool = False ) -> List[capnp._DynamicStructReader]: ``` @@ -73,14 +73,14 @@ output_logs = replay_process_with_name('locationd', lr) output_logs = replay_process_with_name(['ubloxd', 'locationd'], lr) ``` -Supported processes: +Supported processes: * controlsd * radard * plannerd * calibrationd * dmonitoringd * locationd -* paramsd +* paramsd * ubloxd * torqued * modeld From f1c0109c89f8e85d23c9addd11fc7412c3ca4dc7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 15 May 2025 02:18:08 +0800 Subject: [PATCH 220/477] system/ui: Fix crash when exiting the application with CTRL+C (KeyboardInterrupt). (#35221) handle KeyboardInterrupt --- system/ui/lib/application.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index ba1441170b..fb7f7832a4 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -131,17 +131,20 @@ class GuiApplication: rl.close_window() def render(self): - while not (self._window_close_requested or rl.window_should_close()): - rl.begin_drawing() - rl.clear_background(rl.BLACK) + try: + while not (self._window_close_requested or rl.window_should_close()): + rl.begin_drawing() + rl.clear_background(rl.BLACK) - yield + yield - if DEBUG_FPS: - rl.draw_fps(10, 10) + if DEBUG_FPS: + rl.draw_fps(10, 10) - rl.end_drawing() - self._monitor_fps() + rl.end_drawing() + self._monitor_fps() + except KeyboardInterrupt: + pass def font(self, font_weight: FontWeight=FontWeight.NORMAL): return self._fonts[font_weight] From 84e9b7598346dc9970d96ca5576dac35f29ebe37 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 15 May 2025 02:20:39 +0800 Subject: [PATCH 221/477] system/ui: fix WiFi manager shutdown task errors (#35204) fix WiFi manager shutdown task errors --- system/ui/lib/wifi_manager.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index e08e4cbb62..7070266aab 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -115,7 +115,7 @@ class WifiManager: except asyncio.CancelledError: pass if self.bus: - await self.bus.disconnect() + self.bus.disconnect() async def request_scan(self) -> None: try: @@ -634,8 +634,10 @@ class WifiManagerWrapper: def shutdown(self) -> None: if self._running: - if self._manager is not None: - self._run_coroutine(self._manager.shutdown()) + if self._manager is not None and self._loop: + shutdown_future = asyncio.run_coroutine_threadsafe(self._manager.shutdown(), self._loop) + shutdown_future.result(timeout=3.0) + if self._loop and self._loop.is_running(): self._loop.call_soon_threadsafe(self._loop.stop) if self._thread and self._thread.is_alive(): From 02a251f50d8dd157ec2b40c82dbff3c563eb987b Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 15 May 2025 02:21:28 +0800 Subject: [PATCH 222/477] cabana: add RouteInfo dialog to view and navigate route segment details (#35134) add RouteInfo dialog to view and navigate route segment details --- tools/cabana/SConscript | 2 +- tools/cabana/tools/routeinfo.cc | 40 +++++++++++++++++++++++++++++++++ tools/cabana/tools/routeinfo.h | 8 +++++++ tools/cabana/videowidget.cc | 15 +++++++++++-- tools/cabana/videowidget.h | 1 + 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 tools/cabana/tools/routeinfo.cc create mode 100644 tools/cabana/tools/routeinfo.h diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 1cacaba4a2..c8e6093b86 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -29,7 +29,7 @@ cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/socketcans 'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', 'utils/export.cc', 'utils/util.cc', 'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc', - 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) + 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc', 'tools/routeinfo.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) cabana_env.Program('cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('extras'): diff --git a/tools/cabana/tools/routeinfo.cc b/tools/cabana/tools/routeinfo.cc new file mode 100644 index 0000000000..77a0e065cd --- /dev/null +++ b/tools/cabana/tools/routeinfo.cc @@ -0,0 +1,40 @@ +#include "tools/cabana/tools/routeinfo.h" +#include +#include +#include +#include +#include "tools/cabana/streams/replaystream.h" + +RouteInfoDlg::RouteInfoDlg(QWidget *parent) : QDialog(parent) { + auto *replay = qobject_cast(can)->getReplay(); + setWindowTitle(tr("Route: %1").arg(QString::fromStdString(replay->route().name()))); + + auto *table = new QTableWidget(replay->route().segments().size(), 7, this); + table->setToolTip(tr("Click on a row to seek to the corresponding segment.")); + table->setEditTriggers(QAbstractItemView::NoEditTriggers); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + table->setSelectionMode(QAbstractItemView::SingleSelection); + table->setHorizontalHeaderLabels({"", "rlog", "fcam", "ecam", "dcam", "qlog", "qcam"}); + table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + table->verticalHeader()->setVisible(false); + table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + int row = 0; + for (const auto &[seg_num, seg] : replay->route().segments()) { + table->setItem(row, 0, new QTableWidgetItem(QString::number(seg_num))); + table->setItem(row, 1, new QTableWidgetItem(seg.rlog.empty() ? "--" : "Yes")); + table->setItem(row, 2, new QTableWidgetItem(seg.road_cam.empty() ? "--" : "Yes")); + table->setItem(row, 3, new QTableWidgetItem(seg.wide_road_cam.empty() ? "--" : "Yes")); + table->setItem(row, 4, new QTableWidgetItem(seg.driver_cam.empty() ? "--" : "Yes")); + table->setItem(row, 5, new QTableWidgetItem(seg.qlog.empty() ? "--" : "Yes")); + table->setItem(row, 6, new QTableWidgetItem(seg.qcamera.empty() ? "--" : "Yes")); + ++row; + } + table->setMinimumWidth(table->horizontalHeader()->length() + table->verticalScrollBar()->sizeHint().width()); + table->setMinimumHeight(table->rowHeight(0) * std::min(table->rowCount(), 13) + table->horizontalHeader()->height() + table->frameWidth() * 2); + + connect(table, &QTableWidget::itemClicked, [](QTableWidgetItem *item) { can->seekTo(item->row() * 60.0); }); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(table); +} diff --git a/tools/cabana/tools/routeinfo.h b/tools/cabana/tools/routeinfo.h new file mode 100644 index 0000000000..36f32b4bf4 --- /dev/null +++ b/tools/cabana/tools/routeinfo.h @@ -0,0 +1,8 @@ +#pragma once +#include + +class RouteInfoDlg : public QDialog { + Q_OBJECT +public: + RouteInfoDlg(QWidget *parent = nullptr); +}; diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index e792f80a41..3d715b0319 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -11,6 +11,8 @@ #include #include +#include "tools/cabana/tools/routeinfo.h" + const int MIN_VIDEO_HEIGHT = 100; const int THUMBNAIL_MARGIN = 3; @@ -100,9 +102,12 @@ void VideoWidget::createPlaybackController() { if (!can->liveStreaming()) { toolbar->addAction(utils::icon("repeat"), tr("Loop playback"), this, &VideoWidget::loopPlaybackClicked); + createSpeedDropdown(toolbar); + toolbar->addSeparator(); + toolbar->addAction(utils::icon("info-circle"), tr("View route details"), this, &VideoWidget::showRouteInfo); + } else { + createSpeedDropdown(toolbar); } - - createSpeedDropdown(toolbar); } void VideoWidget::createSpeedDropdown(QToolBar *toolbar) { @@ -230,6 +235,12 @@ void VideoWidget::showThumbnail(double seconds) { slider->update(); } +void VideoWidget::showRouteInfo() { + RouteInfoDlg *route_info = new RouteInfoDlg(this); + route_info->setAttribute(Qt::WA_DeleteOnClose); + route_info->show(); +} + bool VideoWidget::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::MouseMove) { auto [min_sec, max_sec] = can->timeRange().value_or(std::make_pair(can->minSeconds(), can->maxSeconds())); diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index 1d87a5cfa0..6f756448c0 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -71,6 +71,7 @@ protected: void createSpeedDropdown(QToolBar *toolbar); void loopPlaybackClicked(); void vipcAvailableStreamsUpdated(std::set streams); + void showRouteInfo(); StreamCameraView *cam_widget; QAction *time_display_action = nullptr; From 2ad501cc946b666a2439eb81417bf34d48dcdeeb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 15 May 2025 02:21:36 +0800 Subject: [PATCH 223/477] common/params.cc: Handle EINTR for fsync in Params::put to Improve Robustness (#35142) Handle EINTR for fsync in Params::put to Improve Robustness --- common/params.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/params.cc b/common/params.cc index d1e5a36462..337be814b7 100644 --- a/common/params.cc +++ b/common/params.cc @@ -140,7 +140,7 @@ int Params::put(const char* key, const char* value, size_t value_size) { } // fsync to force persist the changes. - if ((result = fsync(tmp_fd)) < 0) break; + if ((result = HANDLE_EINTR(fsync(tmp_fd))) < 0) break; FileLock file_lock(params_path + "/.lock"); From e8394a27d7e7cad1c63c612a8a4c4bb5806321f9 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 14 May 2025 14:25:27 -0700 Subject: [PATCH 224/477] fix(clip): properly set exit code (#35225) fix: properly set exit code --- tools/clip/run.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/clip/run.py b/tools/clip/run.py index 87a5d79d2e..fce1b02e98 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -5,6 +5,7 @@ import logging import os import platform import shutil +import sys import time from argparse import ArgumentParser, ArgumentTypeError from collections.abc import Sequence @@ -283,6 +284,7 @@ def main(): p.add_argument('-s', '--start', help='start clipping at seconds', type=int) p.add_argument('-t', '--title', help='overlay this title on the video (e.g. "Chill driving across the Golden Gate Bridge")', type=validate_title) args = parse_args(p) + exit_code = 1 try: clip( data_dir=args.data_dir, @@ -295,12 +297,14 @@ def main(): target_mb=args.file_size, title=args.title, ) + exit_code = 0 except KeyboardInterrupt as e: logger.exception('interrupted by user', exc_info=e) except Exception as e: logger.exception('encountered error', exc_info=e) finally: atexit._run_exitfuncs() + sys.exit(exit_code) if __name__ == '__main__': From 3a6491e23a8390371b29547f86d01fd15c463102 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Wed, 14 May 2025 17:46:55 -0400 Subject: [PATCH 225/477] MADS: allow transition from Paused to Enabled at all times (#910) * MADS: silentBrakeHold should not exit control * no longer * allow transition from paused to enabled when long is allowed --- sunnypilot/mads/mads.py | 10 +++++----- sunnypilot/mads/state.py | 2 +- sunnypilot/mads/tests/test_mads_state_machine.py | 2 +- sunnypilot/selfdrive/selfdrived/events.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index d27e2f1fcb..9aeb67972f 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -89,11 +89,6 @@ class ModularAssistiveDrivingSystem: if CS.brakePressed: transition_paused_state() - if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \ - not self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT): - if self.state_machine.state == State.paused: - self.events_sp.add(EventNameSP.silentLkasEnable) - self.events.remove(EventName.preEnableStandstill) self.events.remove(EventName.belowEngageSpeed) self.events.remove(EventName.speedTooLow) @@ -125,6 +120,11 @@ class ModularAssistiveDrivingSystem: if self.selfdrive.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasDisable) + if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \ + not self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT): + if self.state_machine.state == State.paused: + self.events_sp.add(EventNameSP.silentLkasEnable) + self.events.remove(EventName.pcmDisable) self.events.remove(EventName.buttonCancel) self.events.remove(EventName.pedalPressed) diff --git a/sunnypilot/mads/state.py b/sunnypilot/mads/state.py index 89ae506ac8..73240c790c 100644 --- a/sunnypilot/mads/state.py +++ b/sunnypilot/mads/state.py @@ -51,7 +51,7 @@ class StateMachine: if self.state != State.disabled: # user and immediate disable always have priority in a non-disabled state if self.check_contains(ET.USER_DISABLE): - if self._events_sp.has(EventNameSP.silentLkasDisable) or self._events_sp.has(EventNameSP.silentBrakeHold): + if self._events_sp.has(EventNameSP.silentLkasDisable): self.state = State.paused else: self.state = State.disabled diff --git a/sunnypilot/mads/tests/test_mads_state_machine.py b/sunnypilot/mads/tests/test_mads_state_machine.py index 51013b0baf..7bc556a0fc 100644 --- a/sunnypilot/mads/tests/test_mads_state_machine.py +++ b/sunnypilot/mads/tests/test_mads_state_machine.py @@ -73,7 +73,7 @@ class TestMADSStateMachine: self.clear_events() def test_user_disable_to_paused(self): - paused_events = (EventNameSP.silentLkasDisable, EventNameSP.silentBrakeHold) + paused_events = (EventNameSP.silentLkasDisable, ) for state in ALL_STATES: for et in MAINTAIN_STATES[state]: self.events_sp.add(make_event([et, ET.USER_DISABLE])) diff --git a/sunnypilot/selfdrive/selfdrived/events.py b/sunnypilot/selfdrive/selfdrived/events.py index 6813542c0e..979de0a80c 100644 --- a/sunnypilot/selfdrive/selfdrived/events.py +++ b/sunnypilot/selfdrive/selfdrived/events.py @@ -64,7 +64,7 @@ EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = { }, EventNameSP.silentBrakeHold: { - ET.USER_DISABLE: EngagementAlert(AudibleAlert.none), + ET.WARNING: EngagementAlert(AudibleAlert.none), ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"), }, From 33c164629f2270ce3ee40c260231f9deb4ea13cc Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 14 May 2025 15:44:57 -0700 Subject: [PATCH 226/477] fix(clip): ignore unknown Params keys (#35226) * fix: ignore params configured by forks * comment * dont bother logging count or in normal flow * better log --- tools/clip/run.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index fce1b02e98..fb9ffbbaee 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -16,7 +16,7 @@ from typing import Literal from cereal.messaging import SubMaster from openpilot.common.basedir import BASEDIR -from openpilot.common.params import Params +from openpilot.common.params import Params, UnknownKeyName from openpilot.common.prefix import OpenpilotPrefix from openpilot.tools.lib.route import Route from openpilot.tools.lib.logreader import LogReader, ReadMode, comma_api_source @@ -126,8 +126,12 @@ def populate_car_params(route: Route): entries = init_data.params.entries for cp in entries: key, value = cp.key, cp.value - params.put(key, value) - logger.info(f'persisted {len(entries)} CarParam(s)') + try: + params.put(key, value) + except UnknownKeyName: + # forks of openpilot may have other Params keys configured. ignore these + logger.warning(f"unknown Params key '{key}', skipping") + logger.debug('persisted CarParams') def start_proc(args: list[str], env: dict[str, str]): From 2af3cc6093d461096c876882250216985c7bf421 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 14 May 2025 19:31:55 -0700 Subject: [PATCH 227/477] joystick: use ISO acceleration (#35230) ISO max --- tools/joystick/joystickd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/joystick/joystickd.py b/tools/joystick/joystickd.py index b775f18f70..673a5bc1d0 100755 --- a/tools/joystick/joystickd.py +++ b/tools/joystick/joystickd.py @@ -10,7 +10,7 @@ from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog LongCtrlState = car.CarControl.Actuators.LongControlState -MAX_LAT_ACCEL = 2.5 +MAX_LAT_ACCEL = 3.0 def joystickd_thread(): From 5d8c8ae911f9e1bf1f91dc096bf0a67efef0d9e0 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 14 May 2025 19:51:12 -0700 Subject: [PATCH 228/477] Tesla: raised lateral limits (#35231) * bump * bump * update refs --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 7faa3a0811..f960080e28 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 7faa3a0811ddc7bf7ba38f1c8e2d1b6c17342a30 +Subproject commit f960080e286401cdb432709589d582a6c065fd10 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index ab602d4726..c97f9a6bde 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -61bfff810e45b7a1577cfbb572a7b3c233f8551b \ No newline at end of file +bc0a2e6eac422e574b6c670fe493d368df10ef42 \ No newline at end of file From 0fec3656a1a4d619d70920f7e1f36cd63271fdc2 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Thu, 15 May 2025 06:18:58 -0700 Subject: [PATCH 229/477] feat(clip): faster qlog (#35227) * feat(clip): faster qlog * simpler * imports --- tools/clip/run.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index fb9ffbbaee..500c5e7b40 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -19,7 +19,7 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params, UnknownKeyName from openpilot.common.prefix import OpenpilotPrefix from openpilot.tools.lib.route import Route -from openpilot.tools.lib.logreader import LogReader, ReadMode, comma_api_source +from openpilot.tools.lib.logreader import LogReader DEFAULT_OUTPUT = 'output.mp4' DEMO_START = 90 @@ -117,8 +117,7 @@ def parse_args(parser: ArgumentParser): def populate_car_params(route: Route): - segment = route.name.canonical_name + '/0' # only get first segment qlog - lr = LogReader(segment, default_mode=ReadMode.QLOG, source=comma_api_source) + lr = LogReader(route.qlog_paths()[0] if len(route.qlog_paths()) else route.name.canonical_name) init_data = lr.first('initData') assert init_data is not None From 6c03e9472daa49bfd20c9cebaa5c4ffdc8c0cfcd Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Thu, 15 May 2025 16:42:32 +0100 Subject: [PATCH 230/477] system/ui: disabled button styles (#35236) --- system/ui/lib/button.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index 1857fd21cc..dfe75d88bd 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -49,6 +49,9 @@ def gui_button( ) -> int: result = 0 + if button_style in (ButtonStyle.PRIMARY, ButtonStyle.DANGER) and not is_enabled: + button_style = ButtonStyle.NORMAL + # Set background color based on button type bg_color = BUTTON_BACKGROUND_COLORS[button_style] if is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect): From 735c227f3cc668c70af5a3817b1cf063261f880a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 15 May 2025 09:53:20 -0700 Subject: [PATCH 231/477] svg -> png script --- selfdrive/assets/{strip-svg-metadata.sh => prep-svg.sh} | 4 ++++ 1 file changed, 4 insertions(+) rename selfdrive/assets/{strip-svg-metadata.sh => prep-svg.sh} (67%) diff --git a/selfdrive/assets/strip-svg-metadata.sh b/selfdrive/assets/prep-svg.sh similarity index 67% rename from selfdrive/assets/strip-svg-metadata.sh rename to selfdrive/assets/prep-svg.sh index e51dc9481d..a884d734d0 100755 --- a/selfdrive/assets/strip-svg-metadata.sh +++ b/selfdrive/assets/prep-svg.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -e # sudo apt install scour @@ -6,4 +7,7 @@ for svg in $(find icons/ -type f | grep svg$); do # scour doesn't support overwriting input file scour $svg --remove-metadata $svg.tmp mv $svg.tmp $svg + + # convert to PNG + convert -background none -resize 400% -density 384 $svg "${svg%.svg}.png" done From 60113632b4cbada5b3065540e2c8bc2fd88207e6 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Thu, 15 May 2025 10:02:46 -0700 Subject: [PATCH 232/477] feat(clip): delete get_metadata call (#35229) * feat: delete get_metadata call * no one else is using this now --- tools/clip/run.py | 31 ++++++++++++++++++------------- tools/lib/route.py | 7 ------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index 500c5e7b40..f0c4369c6f 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -64,18 +64,23 @@ def escape_ffmpeg_text(value: str): return value -def get_meta_text(route: Route): - metadata = route.get_metadata() - origin_parts = metadata['git_remote'].split('/') +def get_logreader(route: Route): + return LogReader(route.qlog_paths()[0] if len(route.qlog_paths()) else route.name.canonical_name) + + +def get_meta_text(lr: LogReader, route: Route): + init_data = lr.first('initData') + car_params = lr.first('carParams') + origin_parts = init_data.gitRemote.split('/') origin = origin_parts[3] if len(origin_parts) > 3 else 'unknown' return ', '.join([ - f"openpilot v{metadata['version']}", - f"route: {metadata['fullname']}", - f"car: {metadata['platform']}", + f"openpilot v{init_data.version}", + f"route: {route.name.canonical_name}", + f"car: {car_params.carFingerprint}", f"origin: {origin}", - f"branch: {metadata['git_branch']}", - f"commit: {metadata['git_commit'][:7]}", - f"modified: {str(metadata['git_dirty']).lower()}", + f"branch: {init_data.gitBranch}", + f"commit: {init_data.gitCommit[:7]}", + f"modified: {str(init_data.dirty).lower()}", ]) @@ -116,8 +121,7 @@ def parse_args(parser: ArgumentParser): return args -def populate_car_params(route: Route): - lr = LogReader(route.qlog_paths()[0] if len(route.qlog_paths()) else route.name.canonical_name) +def populate_car_params(lr: LogReader): init_data = lr.first('initData') assert init_data is not None @@ -188,6 +192,7 @@ def clip( title: str | None, ): logger.info(f'clipping route {route.name.canonical_name}, start={start} end={end} quality={quality} target_filesize={target_mb}MB') + lr = get_logreader(route) begin_at = max(start - SECONDS_TO_WARM, 0) duration = end - start @@ -197,7 +202,7 @@ def clip( display = f':{randint(99, 999)}' box_style = 'box=1:boxcolor=black@0.33:boxborderw=7' - meta_text = get_meta_text(route) + meta_text = get_meta_text(lr, route) overlays = [ # metadata overlay f"drawtext=text='{escape_ffmpeg_text(meta_text)}':fontfile={OPENPILOT_FONT}:fontcolor=white:fontsize=15:{box_style}:x=(w-text_w)/2:y=5.5:enable='between(t,1,5)'", @@ -240,7 +245,7 @@ def clip( xvfb_cmd = ['Xvfb', display, '-terminate', '-screen', '0', f'{RESOLUTION}x{PIXEL_DEPTH}'] with OpenpilotPrefix(prefix, shared_download_cache=True): - populate_car_params(route) + populate_car_params(lr) env = os.environ.copy() env['DISPLAY'] = display diff --git a/tools/lib/route.py b/tools/lib/route.py index 831a1e2ee5..ddb8b0486e 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -21,7 +21,6 @@ class Route: def __init__(self, name, data_dir=None): self._name = RouteName(name) self.files = None - self.metadata = None if data_dir is not None: self._segments = self._get_segments_local(data_dir) else: @@ -60,12 +59,6 @@ class Route: qcamera_path_by_seg_num = {s.name.segment_num: s.qcamera_path for s in self._segments} return [qcamera_path_by_seg_num.get(i, None) for i in range(self.max_seg_number + 1)] - def get_metadata(self): - if not self.metadata: - api = CommaApi(get_token()) - self.metadata = api.get('v1/route/' + self.name.canonical_name) - return self.metadata - # TODO: refactor this, it's super repetitive def _get_segments_remote(self): api = CommaApi(get_token()) From ec208f2d3d38fae90f988fdf92362605c3892e48 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 00:26:13 +0100 Subject: [PATCH 233/477] cleanup selfdrive/assets (#35241) * remove unused assets * these are icons * these are images * fix * fix * Revert "remove unused assets" This reverts commit 95d1a800de7e830df15d88e8e329aeadd4548b2f. * move these too --- selfdrive/assets/assets.qrc | 24 +++++++++---------- .../calibration.png} | 0 .../checkmark.svg} | 0 .../chevron_right.png} | 0 .../chffr_wheel.png} | 0 .../circled_check.svg} | 0 .../circled_slash.svg} | 0 .../icon_close.svg => icons/close2.svg} | 0 .../assets/{img_couch.svg => icons/couch.svg} | 0 .../disengage_on_accelerator.svg} | 0 .../driver_face.png} | 0 .../experimental.svg} | 0 .../experimental_grey.svg} | 0 .../experimental_white.svg} | 0 .../eye_closed.svg} | 0 .../{img_eye_open.svg => icons/eye_open.svg} | 0 .../lock_closed.svg} | 0 .../{offroad/icon_menu.png => icons/menu.png} | 0 .../icon_metric.png => icons/metric.png} | 0 .../icon_minus.png => icons/minus.png} | 0 .../monitoring.png} | 0 .../icon_network.png => icons/network.png} | 0 .../{offroad/icon_road.png => icons/road.png} | 0 .../icon_settings.png => icons/settings.png} | 0 .../icon_shell.png => icons/shell.png} | 0 .../speed_limit.png} | 0 .../assets/{images => icons}/triangle.svg | 0 .../icon_warning.png => icons/warning.png} | 0 .../wifi_strength_full.svg} | 0 .../wifi_strength_high.svg} | 0 .../wifi_strength_low.svg} | 0 .../wifi_strength_medium.svg} | 0 .../wifi_uploading.svg} | 0 .../button_continue_triangle.svg} | 0 .../spinner_comma.png} | 0 .../spinner_track.png} | 0 selfdrive/ui/qt/network/networking.cc | 8 +++---- selfdrive/ui/qt/offroad/experimental_mode.cc | 4 ++-- selfdrive/ui/qt/offroad/settings.cc | 18 +++++++------- selfdrive/ui/qt/onroad/buttons.cc | 4 ++-- selfdrive/ui/qt/onroad/driver_monitoring.cc | 2 +- selfdrive/ui/qt/setup/setup.cc | 6 ++--- selfdrive/ui/qt/widgets/input.cc | 4 ++-- system/ui/spinner.py | 4 ++-- 44 files changed, 37 insertions(+), 37 deletions(-) rename selfdrive/assets/{offroad/icon_calibration.png => icons/calibration.png} (100%) rename selfdrive/assets/{offroad/icon_checkmark.svg => icons/checkmark.svg} (100%) rename selfdrive/assets/{offroad/icon_chevron_right.png => icons/chevron_right.png} (100%) rename selfdrive/assets/{img_chffr_wheel.png => icons/chffr_wheel.png} (100%) rename selfdrive/assets/{img_circled_check.svg => icons/circled_check.svg} (100%) rename selfdrive/assets/{img_circled_slash.svg => icons/circled_slash.svg} (100%) rename selfdrive/assets/{offroad/icon_close.svg => icons/close2.svg} (100%) rename selfdrive/assets/{img_couch.svg => icons/couch.svg} (100%) rename selfdrive/assets/{offroad/icon_disengage_on_accelerator.svg => icons/disengage_on_accelerator.svg} (100%) rename selfdrive/assets/{img_driver_face.png => icons/driver_face.png} (100%) rename selfdrive/assets/{img_experimental.svg => icons/experimental.svg} (100%) rename selfdrive/assets/{img_experimental_grey.svg => icons/experimental_grey.svg} (100%) rename selfdrive/assets/{img_experimental_white.svg => icons/experimental_white.svg} (100%) rename selfdrive/assets/{img_eye_closed.svg => icons/eye_closed.svg} (100%) rename selfdrive/assets/{img_eye_open.svg => icons/eye_open.svg} (100%) rename selfdrive/assets/{offroad/icon_lock_closed.svg => icons/lock_closed.svg} (100%) rename selfdrive/assets/{offroad/icon_menu.png => icons/menu.png} (100%) rename selfdrive/assets/{offroad/icon_metric.png => icons/metric.png} (100%) rename selfdrive/assets/{offroad/icon_minus.png => icons/minus.png} (100%) rename selfdrive/assets/{offroad/icon_monitoring.png => icons/monitoring.png} (100%) rename selfdrive/assets/{offroad/icon_network.png => icons/network.png} (100%) rename selfdrive/assets/{offroad/icon_road.png => icons/road.png} (100%) rename selfdrive/assets/{offroad/icon_settings.png => icons/settings.png} (100%) rename selfdrive/assets/{offroad/icon_shell.png => icons/shell.png} (100%) rename selfdrive/assets/{offroad/icon_speed_limit.png => icons/speed_limit.png} (100%) rename selfdrive/assets/{images => icons}/triangle.svg (100%) rename selfdrive/assets/{offroad/icon_warning.png => icons/warning.png} (100%) rename selfdrive/assets/{offroad/icon_wifi_strength_full.svg => icons/wifi_strength_full.svg} (100%) rename selfdrive/assets/{offroad/icon_wifi_strength_high.svg => icons/wifi_strength_high.svg} (100%) rename selfdrive/assets/{offroad/icon_wifi_strength_low.svg => icons/wifi_strength_low.svg} (100%) rename selfdrive/assets/{offroad/icon_wifi_strength_medium.svg => icons/wifi_strength_medium.svg} (100%) rename selfdrive/assets/{offroad/icon_wifi_uploading.svg => icons/wifi_uploading.svg} (100%) rename selfdrive/assets/{img_continue_triangle.svg => images/button_continue_triangle.svg} (100%) rename selfdrive/assets/{img_spinner_comma.png => images/spinner_comma.png} (100%) rename selfdrive/assets/{img_spinner_track.png => images/spinner_track.png} (100%) diff --git a/selfdrive/assets/assets.qrc b/selfdrive/assets/assets.qrc index 6d8d8df334..26a7d998ed 100644 --- a/selfdrive/assets/assets.qrc +++ b/selfdrive/assets/assets.qrc @@ -1,19 +1,19 @@ ../../third_party/bootstrap/bootstrap-icons.svg - img_continue_triangle.svg - img_circled_check.svg - img_circled_slash.svg - img_eye_open.svg - img_eye_closed.svg + images/button_continue_triangle.svg + icons/circled_check.svg + icons/circled_slash.svg + icons/eye_open.svg + icons/eye_closed.svg icons/close.svg - offroad/icon_lock_closed.svg - offroad/icon_checkmark.svg - offroad/icon_warning.png - offroad/icon_wifi_strength_low.svg - offroad/icon_wifi_strength_medium.svg - offroad/icon_wifi_strength_high.svg - offroad/icon_wifi_strength_full.svg + icons/lock_closed.svg + icons/checkmark.svg + icons/warning.png + icons/wifi_strength_low.svg + icons/wifi_strength_medium.svg + icons/wifi_strength_high.svg + icons/wifi_strength_full.svg ../ui/translations/languages.json diff --git a/selfdrive/assets/offroad/icon_calibration.png b/selfdrive/assets/icons/calibration.png similarity index 100% rename from selfdrive/assets/offroad/icon_calibration.png rename to selfdrive/assets/icons/calibration.png diff --git a/selfdrive/assets/offroad/icon_checkmark.svg b/selfdrive/assets/icons/checkmark.svg similarity index 100% rename from selfdrive/assets/offroad/icon_checkmark.svg rename to selfdrive/assets/icons/checkmark.svg diff --git a/selfdrive/assets/offroad/icon_chevron_right.png b/selfdrive/assets/icons/chevron_right.png similarity index 100% rename from selfdrive/assets/offroad/icon_chevron_right.png rename to selfdrive/assets/icons/chevron_right.png diff --git a/selfdrive/assets/img_chffr_wheel.png b/selfdrive/assets/icons/chffr_wheel.png similarity index 100% rename from selfdrive/assets/img_chffr_wheel.png rename to selfdrive/assets/icons/chffr_wheel.png diff --git a/selfdrive/assets/img_circled_check.svg b/selfdrive/assets/icons/circled_check.svg similarity index 100% rename from selfdrive/assets/img_circled_check.svg rename to selfdrive/assets/icons/circled_check.svg diff --git a/selfdrive/assets/img_circled_slash.svg b/selfdrive/assets/icons/circled_slash.svg similarity index 100% rename from selfdrive/assets/img_circled_slash.svg rename to selfdrive/assets/icons/circled_slash.svg diff --git a/selfdrive/assets/offroad/icon_close.svg b/selfdrive/assets/icons/close2.svg similarity index 100% rename from selfdrive/assets/offroad/icon_close.svg rename to selfdrive/assets/icons/close2.svg diff --git a/selfdrive/assets/img_couch.svg b/selfdrive/assets/icons/couch.svg similarity index 100% rename from selfdrive/assets/img_couch.svg rename to selfdrive/assets/icons/couch.svg diff --git a/selfdrive/assets/offroad/icon_disengage_on_accelerator.svg b/selfdrive/assets/icons/disengage_on_accelerator.svg similarity index 100% rename from selfdrive/assets/offroad/icon_disengage_on_accelerator.svg rename to selfdrive/assets/icons/disengage_on_accelerator.svg diff --git a/selfdrive/assets/img_driver_face.png b/selfdrive/assets/icons/driver_face.png similarity index 100% rename from selfdrive/assets/img_driver_face.png rename to selfdrive/assets/icons/driver_face.png diff --git a/selfdrive/assets/img_experimental.svg b/selfdrive/assets/icons/experimental.svg similarity index 100% rename from selfdrive/assets/img_experimental.svg rename to selfdrive/assets/icons/experimental.svg diff --git a/selfdrive/assets/img_experimental_grey.svg b/selfdrive/assets/icons/experimental_grey.svg similarity index 100% rename from selfdrive/assets/img_experimental_grey.svg rename to selfdrive/assets/icons/experimental_grey.svg diff --git a/selfdrive/assets/img_experimental_white.svg b/selfdrive/assets/icons/experimental_white.svg similarity index 100% rename from selfdrive/assets/img_experimental_white.svg rename to selfdrive/assets/icons/experimental_white.svg diff --git a/selfdrive/assets/img_eye_closed.svg b/selfdrive/assets/icons/eye_closed.svg similarity index 100% rename from selfdrive/assets/img_eye_closed.svg rename to selfdrive/assets/icons/eye_closed.svg diff --git a/selfdrive/assets/img_eye_open.svg b/selfdrive/assets/icons/eye_open.svg similarity index 100% rename from selfdrive/assets/img_eye_open.svg rename to selfdrive/assets/icons/eye_open.svg diff --git a/selfdrive/assets/offroad/icon_lock_closed.svg b/selfdrive/assets/icons/lock_closed.svg similarity index 100% rename from selfdrive/assets/offroad/icon_lock_closed.svg rename to selfdrive/assets/icons/lock_closed.svg diff --git a/selfdrive/assets/offroad/icon_menu.png b/selfdrive/assets/icons/menu.png similarity index 100% rename from selfdrive/assets/offroad/icon_menu.png rename to selfdrive/assets/icons/menu.png diff --git a/selfdrive/assets/offroad/icon_metric.png b/selfdrive/assets/icons/metric.png similarity index 100% rename from selfdrive/assets/offroad/icon_metric.png rename to selfdrive/assets/icons/metric.png diff --git a/selfdrive/assets/offroad/icon_minus.png b/selfdrive/assets/icons/minus.png similarity index 100% rename from selfdrive/assets/offroad/icon_minus.png rename to selfdrive/assets/icons/minus.png diff --git a/selfdrive/assets/offroad/icon_monitoring.png b/selfdrive/assets/icons/monitoring.png similarity index 100% rename from selfdrive/assets/offroad/icon_monitoring.png rename to selfdrive/assets/icons/monitoring.png diff --git a/selfdrive/assets/offroad/icon_network.png b/selfdrive/assets/icons/network.png similarity index 100% rename from selfdrive/assets/offroad/icon_network.png rename to selfdrive/assets/icons/network.png diff --git a/selfdrive/assets/offroad/icon_road.png b/selfdrive/assets/icons/road.png similarity index 100% rename from selfdrive/assets/offroad/icon_road.png rename to selfdrive/assets/icons/road.png diff --git a/selfdrive/assets/offroad/icon_settings.png b/selfdrive/assets/icons/settings.png similarity index 100% rename from selfdrive/assets/offroad/icon_settings.png rename to selfdrive/assets/icons/settings.png diff --git a/selfdrive/assets/offroad/icon_shell.png b/selfdrive/assets/icons/shell.png similarity index 100% rename from selfdrive/assets/offroad/icon_shell.png rename to selfdrive/assets/icons/shell.png diff --git a/selfdrive/assets/offroad/icon_speed_limit.png b/selfdrive/assets/icons/speed_limit.png similarity index 100% rename from selfdrive/assets/offroad/icon_speed_limit.png rename to selfdrive/assets/icons/speed_limit.png diff --git a/selfdrive/assets/images/triangle.svg b/selfdrive/assets/icons/triangle.svg similarity index 100% rename from selfdrive/assets/images/triangle.svg rename to selfdrive/assets/icons/triangle.svg diff --git a/selfdrive/assets/offroad/icon_warning.png b/selfdrive/assets/icons/warning.png similarity index 100% rename from selfdrive/assets/offroad/icon_warning.png rename to selfdrive/assets/icons/warning.png diff --git a/selfdrive/assets/offroad/icon_wifi_strength_full.svg b/selfdrive/assets/icons/wifi_strength_full.svg similarity index 100% rename from selfdrive/assets/offroad/icon_wifi_strength_full.svg rename to selfdrive/assets/icons/wifi_strength_full.svg diff --git a/selfdrive/assets/offroad/icon_wifi_strength_high.svg b/selfdrive/assets/icons/wifi_strength_high.svg similarity index 100% rename from selfdrive/assets/offroad/icon_wifi_strength_high.svg rename to selfdrive/assets/icons/wifi_strength_high.svg diff --git a/selfdrive/assets/offroad/icon_wifi_strength_low.svg b/selfdrive/assets/icons/wifi_strength_low.svg similarity index 100% rename from selfdrive/assets/offroad/icon_wifi_strength_low.svg rename to selfdrive/assets/icons/wifi_strength_low.svg diff --git a/selfdrive/assets/offroad/icon_wifi_strength_medium.svg b/selfdrive/assets/icons/wifi_strength_medium.svg similarity index 100% rename from selfdrive/assets/offroad/icon_wifi_strength_medium.svg rename to selfdrive/assets/icons/wifi_strength_medium.svg diff --git a/selfdrive/assets/offroad/icon_wifi_uploading.svg b/selfdrive/assets/icons/wifi_uploading.svg similarity index 100% rename from selfdrive/assets/offroad/icon_wifi_uploading.svg rename to selfdrive/assets/icons/wifi_uploading.svg diff --git a/selfdrive/assets/img_continue_triangle.svg b/selfdrive/assets/images/button_continue_triangle.svg similarity index 100% rename from selfdrive/assets/img_continue_triangle.svg rename to selfdrive/assets/images/button_continue_triangle.svg diff --git a/selfdrive/assets/img_spinner_comma.png b/selfdrive/assets/images/spinner_comma.png similarity index 100% rename from selfdrive/assets/img_spinner_comma.png rename to selfdrive/assets/images/spinner_comma.png diff --git a/selfdrive/assets/img_spinner_track.png b/selfdrive/assets/images/spinner_track.png similarity index 100% rename from selfdrive/assets/img_spinner_track.png rename to selfdrive/assets/images/spinner_track.png diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 066dc3ca7e..93f7ff3f21 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -234,12 +234,12 @@ WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) // load imgs for (const auto &s : {"low", "medium", "high", "full"}) { - QPixmap pix(ASSET_PATH + "/offroad/icon_wifi_strength_" + s + ".svg"); + QPixmap pix(ASSET_PATH + "/icons/wifi_strength_" + s + ".svg"); strengths.push_back(pix.scaledToHeight(68, Qt::SmoothTransformation)); } - lock = QPixmap(ASSET_PATH + "offroad/icon_lock_closed.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); - checkmark = QPixmap(ASSET_PATH + "offroad/icon_checkmark.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); - circled_slash = QPixmap(ASSET_PATH + "img_circled_slash.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); + lock = QPixmap(ASSET_PATH + "icons/lock_closed.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); + checkmark = QPixmap(ASSET_PATH + "icons/checkmark.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); + circled_slash = QPixmap(ASSET_PATH + "icons/circled_slash.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation); scanningLabel = new QLabel(tr("Scanning for networks...")); scanningLabel->setStyleSheet("font-size: 65px;"); diff --git a/selfdrive/ui/qt/offroad/experimental_mode.cc b/selfdrive/ui/qt/offroad/experimental_mode.cc index b99220c1d1..e255073f39 100644 --- a/selfdrive/ui/qt/offroad/experimental_mode.cc +++ b/selfdrive/ui/qt/offroad/experimental_mode.cc @@ -9,8 +9,8 @@ #include "selfdrive/ui/ui.h" ExperimentalModeButton::ExperimentalModeButton(QWidget *parent) : QPushButton(parent) { - chill_pixmap = QPixmap("../assets/img_couch.svg").scaledToWidth(img_width, Qt::SmoothTransformation); - experimental_pixmap = QPixmap("../assets/img_experimental_grey.svg").scaledToWidth(img_width, Qt::SmoothTransformation); + chill_pixmap = QPixmap("../assets/icons/couch.svg").scaledToWidth(img_width, Qt::SmoothTransformation); + experimental_pixmap = QPixmap("../assets/icons/experimental_grey.svg").scaledToWidth(img_width, Qt::SmoothTransformation); // go to toggles and expand experimental mode description connect(this, &QPushButton::clicked, [=]() { emit openSettings(2, "ExperimentalMode"); }); diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 2057f1223e..ed06734f0f 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -23,43 +23,43 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { "OpenpilotEnabledToggle", tr("Enable openpilot"), tr("Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), - "../assets/img_chffr_wheel.png", + "../assets/icons/chffr_wheel.png", }, { "ExperimentalMode", tr("Experimental Mode"), "", - "../assets/img_experimental_white.svg", + "../assets/icons/experimental_white.svg", }, { "DisengageOnAccelerator", tr("Disengage on Accelerator Pedal"), tr("When enabled, pressing the accelerator pedal will disengage openpilot."), - "../assets/offroad/icon_disengage_on_accelerator.svg", + "../assets/icons/disengage_on_accelerator.svg", }, { "IsLdwEnabled", tr("Enable Lane Departure Warnings"), tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), - "../assets/offroad/icon_warning.png", + "../assets/icons/warning.png", }, { "AlwaysOnDM", tr("Always-On Driver Monitoring"), tr("Enable driver monitoring even when openpilot is not engaged."), - "../assets/offroad/icon_monitoring.png", + "../assets/icons/monitoring.png", }, { "RecordFront", tr("Record and Upload Driver Camera"), tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), - "../assets/offroad/icon_monitoring.png", + "../assets/icons/monitoring.png", }, { "IsMetric", tr("Use Metric System"), tr("Display speed in km/h instead of mph."), - "../assets/offroad/icon_metric.png", + "../assets/icons/metric.png", }, }; @@ -69,7 +69,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { tr("Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " "your steering wheel distance button."), - "../assets/offroad/icon_speed_limit.png", + "../assets/icons/speed_limit.png", longi_button_texts); // set up uiState update for personality setting @@ -91,7 +91,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { } // Toggles with confirmation dialogs - toggles["ExperimentalMode"]->setActiveIcon("../assets/img_experimental.svg"); + toggles["ExperimentalMode"]->setActiveIcon("../assets/icons/experimental.svg"); toggles["ExperimentalMode"]->setConfirmation(true, true); } diff --git a/selfdrive/ui/qt/onroad/buttons.cc b/selfdrive/ui/qt/onroad/buttons.cc index 2c2cc672b9..32e58c9dba 100644 --- a/selfdrive/ui/qt/onroad/buttons.cc +++ b/selfdrive/ui/qt/onroad/buttons.cc @@ -19,8 +19,8 @@ void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrus ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) { setFixedSize(btn_size, btn_size); - engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size}); - experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size}); + engage_img = loadPixmap("../assets/icons/chffr_wheel.png", {img_size, img_size}); + experimental_img = loadPixmap("../assets/icons/experimental.svg", {img_size, img_size}); QObject::connect(this, &QPushButton::clicked, this, &ExperimentalButton::changeMode); } diff --git a/selfdrive/ui/qt/onroad/driver_monitoring.cc b/selfdrive/ui/qt/onroad/driver_monitoring.cc index afd003cf8f..49f2c950b4 100644 --- a/selfdrive/ui/qt/onroad/driver_monitoring.cc +++ b/selfdrive/ui/qt/onroad/driver_monitoring.cc @@ -21,7 +21,7 @@ static const QColor DMON_ENGAGED_COLOR = QColor::fromRgbF(0.1, 0.945, 0.26); static const QColor DMON_DISENGAGED_COLOR = QColor::fromRgbF(0.545, 0.545, 0.545); DriverMonitorRenderer::DriverMonitorRenderer() : face_kpts_draw(std::size(DEFAULT_FACE_KPTS_3D)) { - dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5}); + dm_img = loadPixmap("../assets/icons/driver_face.png", {img_size + 5, img_size + 5}); } void DriverMonitorRenderer::updateState(const UIState &s) { diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index dfa9e12b4e..8a06c9fda0 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -98,7 +98,7 @@ QWidget * Setup::low_voltage() { main_layout->addLayout(inner_layout); QLabel *triangle = new QLabel(); - triangle->setPixmap(QPixmap(ASSET_PATH + "offroad/icon_warning.png")); + triangle->setPixmap(QPixmap(ASSET_PATH + "icons/warning.png")); inner_layout->addWidget(triangle, 0, Qt::AlignTop | Qt::AlignLeft); inner_layout->addSpacing(80); @@ -159,7 +159,7 @@ QWidget * Setup::getting_started() { vlayout->addStretch(); QPushButton *btn = new QPushButton(); - btn->setIcon(QIcon(":/img_continue_triangle.svg")); + btn->setIcon(QIcon(":/images/button_continue_triangle.svg")); btn->setIconSize(QSize(54, 106)); btn->setFixedSize(310, 1080); btn->setProperty("primary", true); @@ -251,7 +251,7 @@ QWidget * radio_button(QString title, QButtonGroup *group) { )"); // checkmark icon - QPixmap pix(":/img_circled_check.svg"); + QPixmap pix(":/icons/circled_check.svg"); btn->setIcon(pix); btn->setIconSize(QSize(0, 0)); btn->setLayoutDirection(Qt::RightToLeft); diff --git a/selfdrive/ui/qt/widgets/input.cc b/selfdrive/ui/qt/widgets/input.cc index b0b6b4c23b..0cbf14931b 100644 --- a/selfdrive/ui/qt/widgets/input.cc +++ b/selfdrive/ui/qt/widgets/input.cc @@ -120,11 +120,11 @@ InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &s eye_btn->setFixedSize(150, 120); QObject::connect(eye_btn, &QPushButton::toggled, [=](bool checked) { if (checked) { - eye_btn->setIcon(QIcon(ASSET_PATH + "img_eye_closed.svg")); + eye_btn->setIcon(QIcon(ASSET_PATH + "icons/eye_closed.svg")); eye_btn->setIconSize(QSize(81, 54)); line->setEchoMode(QLineEdit::Password); } else { - eye_btn->setIcon(QIcon(ASSET_PATH + "img_eye_open.svg")); + eye_btn->setIcon(QIcon(ASSET_PATH + "icons/eye_open.svg")); eye_btn->setIconSize(QSize(81, 44)); line->setEchoMode(QLineEdit::Normal); } diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 93bce3887e..5fd3f964a6 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -24,8 +24,8 @@ def clamp(value, min_value, max_value): class SpinnerRenderer: def __init__(self): - self._comma_texture = gui_app.texture("img_spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE) - self._spinner_texture = gui_app.texture("img_spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True) + self._comma_texture = gui_app.texture("images/spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE) + self._spinner_texture = gui_app.texture("images/spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True) self._rotation = 0.0 self._progress: int | None = None self._wrapped_lines: list[str] = [] From 62f5a59f7714b7d1c44a85f3e8fd06a7ce159502 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 00:52:49 +0100 Subject: [PATCH 234/477] selfdrive/assets: optimize SVGs (#35242) --- selfdrive/assets/icons/checkmark.svg | 4 ++-- selfdrive/assets/icons/circled_check.svg | 4 ++-- selfdrive/assets/icons/circled_slash.svg | 4 ++-- selfdrive/assets/icons/close.svg | 4 ++-- selfdrive/assets/icons/close2.svg | 4 ++-- selfdrive/assets/icons/couch.svg | 4 ++-- selfdrive/assets/icons/disengage_on_accelerator.svg | 4 ++-- selfdrive/assets/icons/experimental.svg | 4 ++-- selfdrive/assets/icons/experimental_grey.svg | 4 ++-- selfdrive/assets/icons/experimental_white.svg | 4 ++-- selfdrive/assets/icons/eye_closed.svg | 4 ++-- selfdrive/assets/icons/eye_open.svg | 4 ++-- selfdrive/assets/icons/lock_closed.svg | 4 ++-- selfdrive/assets/icons/triangle.svg | 4 ++-- selfdrive/assets/icons/wifi_strength_full.svg | 4 ++-- selfdrive/assets/icons/wifi_strength_high.svg | 4 ++-- selfdrive/assets/icons/wifi_strength_low.svg | 4 ++-- selfdrive/assets/icons/wifi_strength_medium.svg | 4 ++-- selfdrive/assets/icons/wifi_uploading.svg | 4 ++-- selfdrive/assets/images/button_continue_triangle.svg | 4 ++-- selfdrive/assets/prep-svg.sh | 9 +++------ 21 files changed, 43 insertions(+), 46 deletions(-) diff --git a/selfdrive/assets/icons/checkmark.svg b/selfdrive/assets/icons/checkmark.svg index 7a1db13497..26480698cd 100644 --- a/selfdrive/assets/icons/checkmark.svg +++ b/selfdrive/assets/icons/checkmark.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da1540859c4c42878a32a0a81a38ca38b04be4cb0fe46709df3e024b7f3b4036 -size 243 +oid sha256:17a135c18634647a73734300f5d0ad98082b07779b5553e72b99686857380ee7 +size 244 diff --git a/selfdrive/assets/icons/circled_check.svg b/selfdrive/assets/icons/circled_check.svg index 1852ba947a..aab06ec1e0 100644 --- a/selfdrive/assets/icons/circled_check.svg +++ b/selfdrive/assets/icons/circled_check.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5687faf4cb22bece0405893671651eb13e8eda393987610f493ee5e05eac61d -size 439 +oid sha256:5c88458f6326265965626cbc97c2219bd513b15a6468b08f80b43ef014b7904b +size 372 diff --git a/selfdrive/assets/icons/circled_slash.svg b/selfdrive/assets/icons/circled_slash.svg index 6c77030d15..89c6e2b1ae 100644 --- a/selfdrive/assets/icons/circled_slash.svg +++ b/selfdrive/assets/icons/circled_slash.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbec38447732a443c304042c3c3d362c94e11e794e7cc7dc86aa1c7bba16c6b6 -size 328 +oid sha256:4d91c86028af58cbe2770799d0fe7e55d143f9b3f67fdebcb47d328d6e410285 +size 223 diff --git a/selfdrive/assets/icons/close.svg b/selfdrive/assets/icons/close.svg index 33f68f02bc..e6db01321a 100644 --- a/selfdrive/assets/icons/close.svg +++ b/selfdrive/assets/icons/close.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84c6b23bd3245954b86f80278511186fca4ddfa70d87c8b25e8f9fb76f9af758 -size 379 +oid sha256:a28a4dcaba33d800d109cc5f9a810065203d3bfccd104b2e503f6fe3fc5b6f91 +size 250 diff --git a/selfdrive/assets/icons/close2.svg b/selfdrive/assets/icons/close2.svg index 54a44146d7..56a36cecd5 100644 --- a/selfdrive/assets/icons/close2.svg +++ b/selfdrive/assets/icons/close2.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4eff44a05132ed9f99ad821993be4bab9b1a1c880e07441f3b887214bee62afe -size 825 +oid sha256:7d2a7cd3913ab97a386e946969f6a684895adf5d641c38dfd8f5efa0197a6c58 +size 513 diff --git a/selfdrive/assets/icons/couch.svg b/selfdrive/assets/icons/couch.svg index 7c58515200..f56970f289 100644 --- a/selfdrive/assets/icons/couch.svg +++ b/selfdrive/assets/icons/couch.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a428d2561198ebdc853ba6fb25a8b9c5a58064d000413694e1535adc06633c0a -size 2649 +oid sha256:dd5d8d3fce5e30662797f7eeed224f5775b2cabcc055f01f5ebf6e7b2657b616 +size 1801 diff --git a/selfdrive/assets/icons/disengage_on_accelerator.svg b/selfdrive/assets/icons/disengage_on_accelerator.svg index d5a8c87e21..eef5181935 100644 --- a/selfdrive/assets/icons/disengage_on_accelerator.svg +++ b/selfdrive/assets/icons/disengage_on_accelerator.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5252412f2225c89ea7e78ad0fbd6544170aea157693ce0f9778f26a64f582ec2 -size 6821 +oid sha256:baac01efc894527c8234b774c89cc57b69460d60ad81691a6d50ce0904c60ba7 +size 3638 diff --git a/selfdrive/assets/icons/experimental.svg b/selfdrive/assets/icons/experimental.svg index 3c31caa07e..8a97cdeac1 100644 --- a/selfdrive/assets/icons/experimental.svg +++ b/selfdrive/assets/icons/experimental.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c26afadff128244567a7cf98f1c998f97056b19209301ff7fc8851eb807fb748 -size 2193 +oid sha256:b7ab989c9fe22e7d2119bac5284cdccfba2889477d69f5f6c6b4470c7ee0aab3 +size 1801 diff --git a/selfdrive/assets/icons/experimental_grey.svg b/selfdrive/assets/icons/experimental_grey.svg index 8ba6c87bd5..25ab33f388 100644 --- a/selfdrive/assets/icons/experimental_grey.svg +++ b/selfdrive/assets/icons/experimental_grey.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ae28a53171567c8a0d52eec75cc49004cb8dd19dcab9a360784718ab8ec7c02 -size 1931 +oid sha256:90d441310f8e1833c661d8a4f0546aab2eb50eb21cc21beb0aff0d27b5ee6066 +size 1571 diff --git a/selfdrive/assets/icons/experimental_white.svg b/selfdrive/assets/icons/experimental_white.svg index 9714fe0c01..51d7698947 100644 --- a/selfdrive/assets/icons/experimental_white.svg +++ b/selfdrive/assets/icons/experimental_white.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99be696be983700d7eb1768bd1c840198a4eb9525b71e28efea49f13c358e519 -size 1891 +oid sha256:82648254da89edb0cf65ef63fc5e6e0741414051bfea8a40449132fd61b1ed0f +size 1533 diff --git a/selfdrive/assets/icons/eye_closed.svg b/selfdrive/assets/icons/eye_closed.svg index fcef6e8a3c..a9cb925186 100644 --- a/selfdrive/assets/icons/eye_closed.svg +++ b/selfdrive/assets/icons/eye_closed.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e0e5b451ed2e426fea99da24a9df6552be7048205b9aa7b89e46eeb89f6d08e -size 1490 +oid sha256:fcb0db3b77d6057b94544b6d91b6078fbd91b8f2de189322b90feae3d1ac29da +size 1054 diff --git a/selfdrive/assets/icons/eye_open.svg b/selfdrive/assets/icons/eye_open.svg index 7289c4a571..2befa13adb 100644 --- a/selfdrive/assets/icons/eye_open.svg +++ b/selfdrive/assets/icons/eye_open.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23935f9e2ddba8dafd4a5d0217f29783260a0832c9b0d3e6a2ef66d4529b91d2 -size 775 +oid sha256:411774f4a80831833a344e58932c23a6ebd1db73a6327a63259deca2c6f53613 +size 597 diff --git a/selfdrive/assets/icons/lock_closed.svg b/selfdrive/assets/icons/lock_closed.svg index 501d978a8a..22a510d1c2 100644 --- a/selfdrive/assets/icons/lock_closed.svg +++ b/selfdrive/assets/icons/lock_closed.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef09792cc1893f81c64abd6b72091d3762c07b849863ce90508afdd29b392c02 -size 732 +oid sha256:64e8fc90b79c725a8bf5bbc99643989ae885570997092762e216738805649ade +size 492 diff --git a/selfdrive/assets/icons/triangle.svg b/selfdrive/assets/icons/triangle.svg index f3a8db44b7..233eb5e979 100644 --- a/selfdrive/assets/icons/triangle.svg +++ b/selfdrive/assets/icons/triangle.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaf9a1967365f2c641bf7b54a409e32842cee0bfded7ebb01b1a95c4ac9f0154 -size 2163 +oid sha256:a83c9a78673429caf16d02917be4c8afedadff17b6ceb8c248703ad1120116ed +size 394 diff --git a/selfdrive/assets/icons/wifi_strength_full.svg b/selfdrive/assets/icons/wifi_strength_full.svg index c9d22d8961..4137d3cd4c 100644 --- a/selfdrive/assets/icons/wifi_strength_full.svg +++ b/selfdrive/assets/icons/wifi_strength_full.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:485dd0d4eb8968726003ae460bab4ff498127def52b8b1ed6d968f4629ab233a -size 1655 +oid sha256:3c16c005d666dea64ba6f85de50dfcf161353176eca3d9f475677cbc04fd0382 +size 1161 diff --git a/selfdrive/assets/icons/wifi_strength_high.svg b/selfdrive/assets/icons/wifi_strength_high.svg index ff001bd14a..17d721fe31 100644 --- a/selfdrive/assets/icons/wifi_strength_high.svg +++ b/selfdrive/assets/icons/wifi_strength_high.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c92fd8bebbe630f991fe3f61f61407f8002d29881698634239be3a6fae2fb4bc -size 1657 +oid sha256:0f207b9a1f4ab6009e261f03edd1d05a9d485217415664cba0253d32381e3a03 +size 1164 diff --git a/selfdrive/assets/icons/wifi_strength_low.svg b/selfdrive/assets/icons/wifi_strength_low.svg index bcc6e83c63..4088866370 100644 --- a/selfdrive/assets/icons/wifi_strength_low.svg +++ b/selfdrive/assets/icons/wifi_strength_low.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e629e3a84dc288278e455be4a8deff95730e24d9a0b894f8c417ac1c409670ab -size 1661 +oid sha256:6835937483e5a539618a94e3ec8fad661ae2f7afb39c958e6c8b007b8f4b9b2c +size 1198 diff --git a/selfdrive/assets/icons/wifi_strength_medium.svg b/selfdrive/assets/icons/wifi_strength_medium.svg index e14ebd238f..f0c029dedd 100644 --- a/selfdrive/assets/icons/wifi_strength_medium.svg +++ b/selfdrive/assets/icons/wifi_strength_medium.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52113c14656483854a814c50c93770f5e6ab7d63b2b5048ddd6fd47fdfd4a7de -size 1659 +oid sha256:7ebb1fb5539d9f8ea9899acf4f9cb359503dbe22cd38a3461ad43936348475b9 +size 1167 diff --git a/selfdrive/assets/icons/wifi_uploading.svg b/selfdrive/assets/icons/wifi_uploading.svg index b9392dfe7c..07a14a59f4 100644 --- a/selfdrive/assets/icons/wifi_uploading.svg +++ b/selfdrive/assets/icons/wifi_uploading.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bedd579f56c65fffe2ad571f92843b6d103826fe173f8f5b58fc2200d2ad8850 -size 1663 +oid sha256:e7cfefbda22b53f7eb72a25893ab41c00dabea49f690cafb1d224375a669e608 +size 1170 diff --git a/selfdrive/assets/images/button_continue_triangle.svg b/selfdrive/assets/images/button_continue_triangle.svg index ee9db369a4..e6d362927c 100644 --- a/selfdrive/assets/images/button_continue_triangle.svg +++ b/selfdrive/assets/images/button_continue_triangle.svg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1764c0c93703481b2ced63bc35c5a23a6c12388ca690ce4cf577b3b478a08b69 -size 197 +oid sha256:8084e5a8bbc16956a98b010092ae1c4e32b3391b0551fa1cd65cb1e2bb59d3df +size 169 diff --git a/selfdrive/assets/prep-svg.sh b/selfdrive/assets/prep-svg.sh index a884d734d0..44afa369c1 100755 --- a/selfdrive/assets/prep-svg.sh +++ b/selfdrive/assets/prep-svg.sh @@ -1,13 +1,10 @@ #!/usr/bin/env bash set -e -# sudo apt install scour - -for svg in $(find icons/ -type f | grep svg$); do - # scour doesn't support overwriting input file - scour $svg --remove-metadata $svg.tmp - mv $svg.tmp $svg +for svg in $(find icons/ images/ -type f | grep svg$); do + bunx svgo $svg --multipass --pretty --indent 2 # convert to PNG + # sudo apt install inkscape convert -background none -resize 400% -density 384 $svg "${svg%.svg}.png" done From 83679bd856158b838fefb3070b8adbfdb7d8ebbe Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 01:33:16 +0100 Subject: [PATCH 235/477] selfdrive/assets: rasterize SVGs (#35243) * selfdrive/assets: rasterize SVGs * do entire assets dir * fixed size - 4096px is a lot * optimise them too * use inkscape * regenerate * abandon runtime svg --- selfdrive/assets/icons/checkmark.png | 3 +++ selfdrive/assets/icons/circled_check.png | 3 +++ selfdrive/assets/icons/circled_slash.png | 3 +++ selfdrive/assets/icons/close.png | 3 +++ selfdrive/assets/icons/close2.png | 3 +++ selfdrive/assets/icons/couch.png | 3 +++ .../assets/icons/disengage_on_accelerator.png | 3 +++ selfdrive/assets/icons/experimental.png | 3 +++ selfdrive/assets/icons/experimental_grey.png | 3 +++ selfdrive/assets/icons/experimental_white.png | 3 +++ selfdrive/assets/icons/eye_closed.png | 3 +++ selfdrive/assets/icons/eye_open.png | 3 +++ selfdrive/assets/icons/lock_closed.png | 3 +++ selfdrive/assets/icons/triangle.png | 3 +++ selfdrive/assets/icons/wifi_strength_full.png | 3 +++ selfdrive/assets/icons/wifi_strength_high.png | 3 +++ selfdrive/assets/icons/wifi_strength_low.png | 3 +++ .../assets/icons/wifi_strength_medium.png | 3 +++ selfdrive/assets/icons/wifi_uploading.png | 3 +++ .../images/button_continue_triangle.png | 3 +++ selfdrive/assets/prep-svg.sh | 19 ++++++++++++++++--- system/ui/lib/application.py | 9 +-------- 22 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 selfdrive/assets/icons/checkmark.png create mode 100644 selfdrive/assets/icons/circled_check.png create mode 100644 selfdrive/assets/icons/circled_slash.png create mode 100644 selfdrive/assets/icons/close.png create mode 100644 selfdrive/assets/icons/close2.png create mode 100644 selfdrive/assets/icons/couch.png create mode 100644 selfdrive/assets/icons/disengage_on_accelerator.png create mode 100644 selfdrive/assets/icons/experimental.png create mode 100644 selfdrive/assets/icons/experimental_grey.png create mode 100644 selfdrive/assets/icons/experimental_white.png create mode 100644 selfdrive/assets/icons/eye_closed.png create mode 100644 selfdrive/assets/icons/eye_open.png create mode 100644 selfdrive/assets/icons/lock_closed.png create mode 100644 selfdrive/assets/icons/triangle.png create mode 100644 selfdrive/assets/icons/wifi_strength_full.png create mode 100644 selfdrive/assets/icons/wifi_strength_high.png create mode 100644 selfdrive/assets/icons/wifi_strength_low.png create mode 100644 selfdrive/assets/icons/wifi_strength_medium.png create mode 100644 selfdrive/assets/icons/wifi_uploading.png create mode 100644 selfdrive/assets/images/button_continue_triangle.png diff --git a/selfdrive/assets/icons/checkmark.png b/selfdrive/assets/icons/checkmark.png new file mode 100644 index 0000000000..0f9a802a35 --- /dev/null +++ b/selfdrive/assets/icons/checkmark.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc472f0e575e314c4006cb6e9845fb9fb9a13cf08fe74fbe1593dee53c20d977 +size 4329 diff --git a/selfdrive/assets/icons/circled_check.png b/selfdrive/assets/icons/circled_check.png new file mode 100644 index 0000000000..7611bc821f --- /dev/null +++ b/selfdrive/assets/icons/circled_check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdb0be280ac3a78bf95f5b92fafe94de5084ecc06836459c3a9fe1912a5b2454 +size 10479 diff --git a/selfdrive/assets/icons/circled_slash.png b/selfdrive/assets/icons/circled_slash.png new file mode 100644 index 0000000000..74a9b342f6 --- /dev/null +++ b/selfdrive/assets/icons/circled_slash.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2a992a83eaa87762e12dc226f36af48e1cdbfc3b83ef75b6a2fc4103e3697a0 +size 9120 diff --git a/selfdrive/assets/icons/close.png b/selfdrive/assets/icons/close.png new file mode 100644 index 0000000000..66d1545632 --- /dev/null +++ b/selfdrive/assets/icons/close.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c11f831c17080a8ffaa8469cf91a079a4abfb72e5238afe02b92bceb3442db0 +size 2656 diff --git a/selfdrive/assets/icons/close2.png b/selfdrive/assets/icons/close2.png new file mode 100644 index 0000000000..4497c97547 --- /dev/null +++ b/selfdrive/assets/icons/close2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbfed12ddb5731b7b568539fe59f382356b46fdd146a9e0a1b768d3e5efd0378 +size 4350 diff --git a/selfdrive/assets/icons/couch.png b/selfdrive/assets/icons/couch.png new file mode 100644 index 0000000000..677ad0f9ee --- /dev/null +++ b/selfdrive/assets/icons/couch.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:911f59f248015600da7ecc689398103d47dfc57f6be17ac8c8e543a726a6c64b +size 3311 diff --git a/selfdrive/assets/icons/disengage_on_accelerator.png b/selfdrive/assets/icons/disengage_on_accelerator.png new file mode 100644 index 0000000000..4134834448 --- /dev/null +++ b/selfdrive/assets/icons/disengage_on_accelerator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f23fabbf60fff6ef88ba6f27f2775b1ae6be172a994e41267983a9ec0f984bfc +size 15059 diff --git a/selfdrive/assets/icons/experimental.png b/selfdrive/assets/icons/experimental.png new file mode 100644 index 0000000000..2332fe11d0 --- /dev/null +++ b/selfdrive/assets/icons/experimental.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e58deb1778cf2826339f27e9f09eecc79ea137c1436210c14b71c352a649c77 +size 34953 diff --git a/selfdrive/assets/icons/experimental_grey.png b/selfdrive/assets/icons/experimental_grey.png new file mode 100644 index 0000000000..058c3e1358 --- /dev/null +++ b/selfdrive/assets/icons/experimental_grey.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f37a02dd914405c6f86f415700dd5985eb976b923e7abd6580d2da76533594e +size 9466 diff --git a/selfdrive/assets/icons/experimental_white.png b/selfdrive/assets/icons/experimental_white.png new file mode 100644 index 0000000000..6a9cfc4407 --- /dev/null +++ b/selfdrive/assets/icons/experimental_white.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b2dad33fead9f064c3a548651d6ef37daf82b6127c329683f538ec6e986ecbc +size 11204 diff --git a/selfdrive/assets/icons/eye_closed.png b/selfdrive/assets/icons/eye_closed.png new file mode 100644 index 0000000000..8eba02e600 --- /dev/null +++ b/selfdrive/assets/icons/eye_closed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64d9dc106172d3a54088ef51a27a48145154ca040c43ecbc8d626fa42e38886e +size 9352 diff --git a/selfdrive/assets/icons/eye_open.png b/selfdrive/assets/icons/eye_open.png new file mode 100644 index 0000000000..9783716ff3 --- /dev/null +++ b/selfdrive/assets/icons/eye_open.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94246db66e774cbaee618931f76ecc38ecb72eca097d1e6c20a8dec2a5f8cd29 +size 7087 diff --git a/selfdrive/assets/icons/lock_closed.png b/selfdrive/assets/icons/lock_closed.png new file mode 100644 index 0000000000..21e6795737 --- /dev/null +++ b/selfdrive/assets/icons/lock_closed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b89b8803bb610515aef051c93b833dc62f8c847558873cfd50a0b240c968449 +size 4911 diff --git a/selfdrive/assets/icons/triangle.png b/selfdrive/assets/icons/triangle.png new file mode 100644 index 0000000000..47ff24f200 --- /dev/null +++ b/selfdrive/assets/icons/triangle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f2745ce89c926e507888ea7c6df1884aab045887048cf0d813407396a2e6b18 +size 5894 diff --git a/selfdrive/assets/icons/wifi_strength_full.png b/selfdrive/assets/icons/wifi_strength_full.png new file mode 100644 index 0000000000..1a710f2967 --- /dev/null +++ b/selfdrive/assets/icons/wifi_strength_full.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b7f0971cf612b905ccb338e40921932773538517fc9f0f7a4a847ad596287a9 +size 7171 diff --git a/selfdrive/assets/icons/wifi_strength_high.png b/selfdrive/assets/icons/wifi_strength_high.png new file mode 100644 index 0000000000..2d360968b6 --- /dev/null +++ b/selfdrive/assets/icons/wifi_strength_high.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2afacf302bcdc3f5e0d2f734508e8fea6f803813098d52fa6b878c765ba7a58 +size 9360 diff --git a/selfdrive/assets/icons/wifi_strength_low.png b/selfdrive/assets/icons/wifi_strength_low.png new file mode 100644 index 0000000000..d016528304 --- /dev/null +++ b/selfdrive/assets/icons/wifi_strength_low.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dfe50815c76d459dc104ce4a5e4b5dfd882e11b65e07706cacd336e69e788f4 +size 9756 diff --git a/selfdrive/assets/icons/wifi_strength_medium.png b/selfdrive/assets/icons/wifi_strength_medium.png new file mode 100644 index 0000000000..9c943543a4 --- /dev/null +++ b/selfdrive/assets/icons/wifi_strength_medium.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:304ab97ac9724a7a133a36c2d14da9fe8ab660c9a29abb99cc0a4828f94d8801 +size 9627 diff --git a/selfdrive/assets/icons/wifi_uploading.png b/selfdrive/assets/icons/wifi_uploading.png new file mode 100644 index 0000000000..19d9bf36ce --- /dev/null +++ b/selfdrive/assets/icons/wifi_uploading.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8eac201013322db1580649a253da7ae38eb9f16f6089234e769b746951e874ee +size 7171 diff --git a/selfdrive/assets/images/button_continue_triangle.png b/selfdrive/assets/images/button_continue_triangle.png new file mode 100644 index 0000000000..c56e112094 --- /dev/null +++ b/selfdrive/assets/images/button_continue_triangle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9218e02c42b0f80858477255e24a97da4cf0b2898fc76f3806409d65b104668 +size 4510 diff --git a/selfdrive/assets/prep-svg.sh b/selfdrive/assets/prep-svg.sh index 44afa369c1..661464a5c6 100755 --- a/selfdrive/assets/prep-svg.sh +++ b/selfdrive/assets/prep-svg.sh @@ -1,10 +1,23 @@ #!/usr/bin/env bash set -e -for svg in $(find icons/ images/ -type f | grep svg$); do +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" + +# sudo apt install inkscape + +for svg in $(find $DIR -type f | grep svg$); do bunx svgo $svg --multipass --pretty --indent 2 # convert to PNG - # sudo apt install inkscape - convert -background none -resize 400% -density 384 $svg "${svg%.svg}.png" + png="${svg%.svg}.png" + width=$(inkscape --query-width "$svg") + height=$(inkscape --query-height "$svg") + if (( $(echo "$width > $height" | bc -l) )); then + export_dim="--export-width=512" + else + export_dim="--export-height=512" + fi + inkscape "$svg" --export-filename="$png" $export_dim + + optipng -o7 -strip all "$png" done diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index fb7f7832a4..8961bb0327 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -80,10 +80,7 @@ class GuiApplication: def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): """Load and resize a texture, storing it for later automatic unloading.""" - if image_path.endswith('.svg'): - image = self._load_image_from_svg(image_path) - else: - image = rl.load_image(image_path) + image = rl.load_image(image_path) if alpha_premultiply: rl.image_alpha_premultiply(image) @@ -112,10 +109,6 @@ class GuiApplication: rl.unload_image(image) return texture - def _load_image_from_svg(self, svg_path: str): - # TODO: Implement SVG loading - assert(0) - def close(self): if not rl.is_window_ready(): return From 1e74000f79de4843197b3c9b031707026d33af8a Mon Sep 17 00:00:00 2001 From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Date: Thu, 15 May 2025 20:13:40 -0700 Subject: [PATCH 236/477] Controls: Extension (#858) * Add enhanced HyundaiCAN extension Introduced HyundaiCanEXT for improved object parsing and control integration, leveraging new fields like lead distance and relative speed in CarControlSP. Refactored controlsd to utilize a modular design with ControlsdExt for additional sunnypilot specific functionality. * self.cc_sp * Refactor CarControlSP handling for improved state updates * refactor * bool * Refactor controlsd SP communication logic Update `publish_sp` method to include `CC_SP` parameter and refine SP SubMaster to exclude `radarState`. Remove custom lead vehicle state processing as it is no longer needed. * remove in this pr * bump * start cleanup * inherit instead * even more! * lint * type hint * use the same objects for submaster and pubmaster * more --------- Co-authored-by: Jason Wen --- cereal/custom.capnp | 10 ++-- selfdrive/controls/controlsd.py | 40 ++++++-------- .../selfdrive/controls/controlsd_ext.py | 53 +++++++++++++++++++ 3 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 sunnypilot/selfdrive/controls/controlsd_ext.py diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 10fc83eb01..2a72a59a10 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -63,7 +63,7 @@ struct ModelManagerSP @0xaedffd8f31e7b55d { type @0 :Type; artifact @1 :Artifact; # Main artifact metadata @2 :Artifact; # Metadata artifact - + enum Type { supercombo @0; navigation @1; @@ -176,14 +176,14 @@ struct BackupManagerSP @0xf98d843bfd7004a3 { lastError @4 :Text; currentBackup @5 :BackupInfo; backupHistory @6 :List(BackupInfo); - + enum Status { idle @0; inProgress @1; completed @2; failed @3; } - + struct Version { major @0 :UInt16; minor @1 :UInt16; @@ -191,13 +191,13 @@ struct BackupManagerSP @0xf98d843bfd7004a3 { build @3 :UInt16; branch @4 :Text; } - + struct MetadataEntry { key @0 :Text; value @1 :Text; tags @2 :List(Text); } - + struct BackupInfo { deviceId @0 :Text; version @1 :UInt32; diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 69f468e4cd..a5d97c3db0 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -2,7 +2,7 @@ import math from typing import SupportsFloat -from cereal import car, log, custom +from cereal import car, log import cereal.messaging as messaging from openpilot.common.conversions import Conversions as CV from openpilot.common.params import Params @@ -19,6 +19,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque from openpilot.selfdrive.controls.lib.longcontrol import LongControl from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose +from openpilot.sunnypilot.selfdrive.controls.controlsd_ext import ControlsExt + State = log.SelfdriveState.OpenpilotState LaneChangeState = log.LaneChangeState LaneChangeDirection = log.LaneChangeDirection @@ -26,24 +28,23 @@ LaneChangeDirection = log.LaneChangeDirection ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys()) -class Controls: +class Controls(ControlsExt): def __init__(self) -> None: self.params = Params() cloudlog.info("controlsd is waiting for CarParams") self.CP = messaging.log_from_bytes(self.params.get("CarParams", block=True), car.CarParams) cloudlog.info("controlsd got CarParams") - cloudlog.info("controlsd is waiting for CarParamsSP") - self.CP_SP = messaging.log_from_bytes(self.params.get("CarParamsSP", block=True), custom.CarParamsSP) - cloudlog.info("controlsd got CarParamsSP") + # Initialize sunnypilot controlsd extension + ControlsExt.__init__(self, self.params) self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP) self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState', 'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput', - 'driverMonitoringState', 'onroadEvents', 'driverAssistance'] + ['selfdriveStateSP'], + 'driverMonitoringState', 'onroadEvents', 'driverAssistance'] + self.sm_services_ext, poll='selfdriveState') - self.pm = messaging.PubMaster(['carControl', 'controlsState'] + ['carControlSP']) + self.pm = messaging.PubMaster(['carControl', 'controlsState'] + self.pm_services_ext) self.steer_limited_by_controls = False self.curvature = 0.0 @@ -100,11 +101,8 @@ class Controls: # Check which actuators can be enabled standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, 0.3) or CS.standstill - ss_sp = self.sm['selfdriveStateSP'] - if ss_sp.mads.available: - _lat_active = ss_sp.mads.active - else: - _lat_active = self.sm['selfdriveState'].active + # Get which state to use for active lateral control + _lat_active = self.get_lat_active(self.sm) CC.latActive = _lat_active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and \ (not standstill or self.CP.steerAtStandstill) @@ -148,12 +146,9 @@ class Controls: cloudlog.error(f"actuators.{p} not finite {actuators.to_dict()}") setattr(actuators, p, 0.0) - CC_SP = custom.CarControlSP.new_message() - CC_SP.mads = ss_sp.mads + return CC, lac_log - return CC, CC_SP, lac_log - - def publish(self, CC, CC_SP, lac_log): + def publish(self, CC, lac_log): CS = self.sm['carState'] # Orientation and angle rates can be useful for carcontroller @@ -227,18 +222,13 @@ class Controls: cc_send.carControl = CC self.pm.send('carControl', cc_send) - # carControlSP - cc_sp_send = messaging.new_message('carControlSP') - cc_sp_send.valid = CS.canValid - cc_sp_send.carControlSP = CC_SP - self.pm.send('carControlSP', cc_sp_send) - def run(self): rk = Ratekeeper(100, print_delay_threshold=None) while True: self.update() - CC, CC_SP, lac_log = self.state_control() - self.publish(CC, CC_SP, lac_log) + CC, lac_log = self.state_control() + self.publish(CC, lac_log) + self.run_ext(self.sm, self.pm) rk.monitor_time() diff --git a/sunnypilot/selfdrive/controls/controlsd_ext.py b/sunnypilot/selfdrive/controls/controlsd_ext.py new file mode 100644 index 0000000000..68131001ad --- /dev/null +++ b/sunnypilot/selfdrive/controls/controlsd_ext.py @@ -0,0 +1,53 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" + +import cereal.messaging as messaging +from cereal import custom + +from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog + + +class ControlsExt: + def __init__(self, params: Params): + cloudlog.info("controlsd_ext is waiting for CarParamsSP") + self.CP_SP = messaging.log_from_bytes(params.get("CarParamsSP", block=True), custom.CarParamsSP) + cloudlog.info("controlsd_ext got CarParamsSP") + + self.sm_services_ext = ['selfdriveStateSP'] + self.pm_services_ext = ['carControlSP'] + + @staticmethod + def get_lat_active(sm: messaging.SubMaster) -> bool: + ss_sp = sm['selfdriveStateSP'] + + if ss_sp.mads.available: + return bool(ss_sp.mads.active) + + # MADS not available, use stock state to engage + return bool(sm['selfdriveState'].active) + + @staticmethod + def state_control_ext(sm: messaging.SubMaster) -> custom.CarControlSP: + CC_SP = custom.CarControlSP.new_message() + + # MADS state + CC_SP.mads = sm['selfdriveStateSP'].mads + + return CC_SP + + @staticmethod + def publish_ext(CC_SP: custom.CarControlSP, sm: messaging.SubMaster, pm: messaging.PubMaster) -> None: + cc_sp_send = messaging.new_message('carControlSP') + cc_sp_send.valid = sm['carState'].canValid + cc_sp_send.carControlSP = CC_SP + + pm.send('carControlSP', cc_sp_send) + + def run_ext(self, sm: messaging.SubMaster, pm: messaging.PubMaster) -> None: + CC_SP = self.state_control_ext(sm) + self.publish_ext(CC_SP, sm, pm) From 512d83cc36e7ade7d9623c81149df945f8764055 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 12:41:20 +0100 Subject: [PATCH 237/477] ui(raylib): setup.py (#35140) * setup.py * better font * use gui_button * btn * fix button and triangle * low voltage text color * fix network page * HARDWARE.get_os_version() * typing * white title * update default text color * use default font color * fix software screen * fix software screen * radio font size * line length * fix regex * draw svgs * comment is out of date * add cairosvg * use cairosvg * remove unused import * support other image types * revert origin * fix setup warning icon * fix * remove cairosvg * use pngs * wrap * fix disabled style * TODO * revert uv.lock * use new file paths (not rasterized yet) * oops * fixes * params not used * network check thread * oops * fix custom URL and download failed screens * clear keyboard * rm * fixes * show full error message * check network type --- system/ui/lib/application.py | 2 +- system/ui/setup.py | 344 +++++++++++++++++++++++++++++++++++ 2 files changed, 345 insertions(+), 1 deletion(-) create mode 100755 system/ui/setup.py diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 8961bb0327..9e6c8ef28d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -17,7 +17,7 @@ DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1' DEFAULT_TEXT_SIZE = 60 -DEFAULT_TEXT_COLOR = rl.Color(200, 200, 200, 255) +DEFAULT_TEXT_COLOR = rl.WHITE ASSETS_DIR = os.path.join(BASEDIR, "selfdrive/assets") FONT_DIR = os.path.join(BASEDIR, "selfdrive/assets/fonts") diff --git a/system/ui/setup.py b/system/ui/setup.py new file mode 100755 index 0000000000..8c75b8e7c7 --- /dev/null +++ b/system/ui/setup.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 +import os +import re +import threading +import time +import urllib.request +from enum import IntEnum +import pyray as rl + +from cereal import log +from openpilot.system.hardware import HARDWARE +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.button import gui_button, ButtonStyle +from openpilot.system.ui.lib.label import gui_label, gui_text_box +from openpilot.system.ui.widgets.network import WifiManagerUI, WifiManagerWrapper +from openpilot.system.ui.widgets.keyboard import Keyboard + +NetworkType = log.DeviceState.NetworkType + +MARGIN = 50 +TITLE_FONT_SIZE = 116 +TITLE_FONT_WEIGHT = FontWeight.MEDIUM +NEXT_BUTTON_WIDTH = 310 +BODY_FONT_SIZE = 96 +BUTTON_HEIGHT = 160 +BUTTON_SPACING = 50 + +OPENPILOT_URL = "https://openpilot.comma.ai" +USER_AGENT = f"AGNOSSetup-{HARDWARE.get_os_version()}" + + +class SetupState(IntEnum): + LOW_VOLTAGE = 0 + GETTING_STARTED = 1 + NETWORK_SETUP = 2 + SOFTWARE_SELECTION = 3 + CUSTOM_URL = 4 + DOWNLOADING = 5 + DOWNLOAD_FAILED = 6 + + +class Setup: + def __init__(self): + self.state = SetupState.GETTING_STARTED + self.network_check_thread = None + self.network_connected = threading.Event() + self.wifi_connected = threading.Event() + self.stop_network_check_thread = threading.Event() + self.failed_url = "" + self.failed_reason = "" + self.download_url = "" + self.download_progress = 0 + self.download_thread = None + self.wifi_manager = WifiManagerWrapper() + self.wifi_ui = WifiManagerUI(self.wifi_manager) + self.keyboard = Keyboard() + self.selected_radio = None + + self.warning = gui_app.texture("icons/warning.png", 150, 150) + self.checkmark = gui_app.texture("icons/circled_check.png", 100, 100) + + try: + with open("/sys/class/hwmon/hwmon1/in1_input") as f: + voltage = float(f.read().strip()) / 1000.0 + if voltage < 7: + self.state = SetupState.LOW_VOLTAGE + except (FileNotFoundError, ValueError): + self.state = SetupState.LOW_VOLTAGE + + def render(self, rect: rl.Rectangle): + if self.state == SetupState.LOW_VOLTAGE: + self.render_low_voltage(rect) + elif self.state == SetupState.GETTING_STARTED: + self.render_getting_started(rect) + elif self.state == SetupState.NETWORK_SETUP: + self.render_network_setup(rect) + elif self.state == SetupState.SOFTWARE_SELECTION: + self.render_software_selection(rect) + elif self.state == SetupState.CUSTOM_URL: + self.render_custom_url() + elif self.state == SetupState.DOWNLOADING: + self.render_downloading(rect) + elif self.state == SetupState.DOWNLOAD_FAILED: + self.render_download_failed(rect) + + def render_low_voltage(self, rect: rl.Rectangle): + rl.draw_texture(self.warning, int(rect.x + 150), int(rect.y + 110), rl.WHITE) + + title_rect = rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100, rect.width - 500 - 150, TITLE_FONT_SIZE) + gui_label(title_rect, "WARNING: Low Voltage", TITLE_FONT_SIZE, rl.Color(255, 89, 79, 255), FontWeight.MEDIUM) + + body_rect = rl.Rectangle(rect.x + 150, rect.y + 110 + 150 + 100 + TITLE_FONT_SIZE + 25, rect.width - 500 - 150, BODY_FONT_SIZE * 3) + gui_text_box(body_rect, "Power your device in a car with a harness or proceed at your own risk.", BODY_FONT_SIZE) + + button_width = (rect.width - MARGIN * 3) / 2 + button_y = rect.height - MARGIN - BUTTON_HEIGHT + + if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Power off"): + HARDWARE.shutdown() + + if gui_button(rl.Rectangle(rect.x + MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT), "Continue"): + self.state = SetupState.GETTING_STARTED + + def render_getting_started(self, rect: rl.Rectangle): + title_rect = rl.Rectangle(rect.x + 165, rect.y + 280, rect.width - 265, TITLE_FONT_SIZE) + gui_label(title_rect, "Getting Started", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + + desc_rect = rl.Rectangle(rect.x + 165, rect.y + 280 + TITLE_FONT_SIZE + 90, rect.width - 500, BODY_FONT_SIZE * 3) + gui_text_box(desc_rect, "Before we get on the road, let's finish installation and cover some details.", BODY_FONT_SIZE) + + btn_rect = rl.Rectangle(rect.width - NEXT_BUTTON_WIDTH, 0, NEXT_BUTTON_WIDTH, rect.height) + + ret = gui_button(btn_rect, "", button_style=ButtonStyle.PRIMARY, border_radius=0) + triangle = gui_app.texture("images/button_continue_triangle.png", 54, int(btn_rect.height)) + rl.draw_texture_v(triangle, rl.Vector2(btn_rect.x + btn_rect.width / 2 - triangle.width / 2, btn_rect.height / 2 - triangle.height / 2), rl.WHITE) + + if ret: + self.state = SetupState.NETWORK_SETUP + self.wifi_manager.request_scan() + self.start_network_check() + + def check_network_connectivity(self): + while not self.stop_network_check_thread.is_set(): + if self.state == SetupState.NETWORK_SETUP: + try: + urllib.request.urlopen("https://google.com", timeout=2) + self.network_connected.set() + if HARDWARE.get_network_type() == NetworkType.wifi: + self.wifi_connected.set() + else: + self.wifi_connected.clear() + except Exception: + self.network_connected.clear() + time.sleep(1) + + def start_network_check(self): + if self.network_check_thread is None or not self.network_check_thread.is_alive(): + self.network_check_thread = threading.Thread(target=self.check_network_connectivity, daemon=True) + self.network_check_thread.start() + + def close(self): + if self.network_check_thread is not None: + self.stop_network_check_thread.set() + self.network_check_thread.join() + + def render_network_setup(self, rect: rl.Rectangle): + title_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE) + gui_label(title_rect, "Connect to Wi-Fi", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + + wifi_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN + 25, rect.width - MARGIN * 2, + rect.height - TITLE_FONT_SIZE - 25 - BUTTON_HEIGHT - MARGIN * 3) + rl.draw_rectangle_rounded(wifi_rect, 0.05, 10, rl.Color(51, 51, 51, 255)) + wifi_content_rect = rl.Rectangle(wifi_rect.x + MARGIN, wifi_rect.y, wifi_rect.width - MARGIN * 2, wifi_rect.height) + self.wifi_ui.render(wifi_content_rect) + + button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 + button_y = rect.height - BUTTON_HEIGHT - MARGIN + + if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Back"): + self.state = SetupState.GETTING_STARTED + + # Check network connectivity status + continue_enabled = self.network_connected.is_set() + continue_text = ("Continue" if self.wifi_connected.is_set() else "Continue without Wi-Fi") if continue_enabled else "Waiting for internet" + + if gui_button( + rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), + continue_text, + button_style=ButtonStyle.PRIMARY if continue_enabled else ButtonStyle.NORMAL, + ): + self.state = SetupState.SOFTWARE_SELECTION + self.stop_network_check_thread.set() + + def render_software_selection(self, rect: rl.Rectangle): + title_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE) + gui_label(title_rect, "Choose Software to Install", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + + radio_height = 230 + radio_spacing = 30 + + openpilot_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2, rect.width - MARGIN * 2, radio_height) + openpilot_selected = self.selected_radio == "openpilot" + + rl.draw_rectangle_rounded(openpilot_rect, 0.1, 10, rl.Color(70, 91, 234, 255) if openpilot_selected else rl.Color(79, 79, 79, 255)) + gui_label(rl.Rectangle(openpilot_rect.x + 100, openpilot_rect.y, openpilot_rect.width - 200, radio_height), "openpilot", BODY_FONT_SIZE) + + if openpilot_selected: + checkmark_pos = rl.Vector2(openpilot_rect.x + openpilot_rect.width - 100 - self.checkmark.width, + openpilot_rect.y + radio_height / 2 - self.checkmark.height / 2) + rl.draw_texture_v(self.checkmark, checkmark_pos, rl.WHITE) + + custom_rect = rl.Rectangle(rect.x + MARGIN, rect.y + TITLE_FONT_SIZE + MARGIN * 2 + radio_height + radio_spacing, rect.width - MARGIN * 2, radio_height) + custom_selected = self.selected_radio == "custom" + + rl.draw_rectangle_rounded(custom_rect, 0.1, 10, rl.Color(70, 91, 234, 255) if custom_selected else rl.Color(79, 79, 79, 255)) + gui_label(rl.Rectangle(custom_rect.x + 100, custom_rect.y, custom_rect.width - 200, radio_height), "Custom Software", BODY_FONT_SIZE) + + if custom_selected: + checkmark_pos = rl.Vector2(custom_rect.x + custom_rect.width - 100 - self.checkmark.width, custom_rect.y + radio_height / 2 - self.checkmark.height / 2) + rl.draw_texture_v(self.checkmark, checkmark_pos, rl.WHITE) + + mouse_pos = rl.get_mouse_position() + if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): + if rl.check_collision_point_rec(mouse_pos, openpilot_rect): + self.selected_radio = "openpilot" + elif rl.check_collision_point_rec(mouse_pos, custom_rect): + self.selected_radio = "custom" + + button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 + button_y = rect.height - BUTTON_HEIGHT - MARGIN + + if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Back"): + self.state = SetupState.NETWORK_SETUP + + continue_enabled = self.selected_radio is not None + if gui_button( + rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), + "Continue", + button_style=ButtonStyle.PRIMARY, + is_enabled=continue_enabled, + ): + if continue_enabled: + if self.selected_radio == "openpilot": + self.download(OPENPILOT_URL) + else: + self.state = SetupState.CUSTOM_URL + + def render_downloading(self, rect: rl.Rectangle): + title_rect = rl.Rectangle(rect.x, rect.y + rect.height / 2 - TITLE_FONT_SIZE / 2, rect.width, TITLE_FONT_SIZE) + gui_label(title_rect, "Downloading...", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + def render_download_failed(self, rect: rl.Rectangle): + title_rect = rl.Rectangle(rect.x + 117, rect.y + 185, rect.width - 117, TITLE_FONT_SIZE) + gui_label(title_rect, "Download Failed", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) + + url_rect = rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE + 67, rect.width - 117 - 100, 64) + gui_label(url_rect, self.failed_url, 64, font_weight=FontWeight.NORMAL) + + error_rect = rl.Rectangle(rect.x + 117, rect.y + 185 + TITLE_FONT_SIZE + 67 + 64 + 48, + rect.width - 117 - 100, rect.height - 185 + TITLE_FONT_SIZE + 67 + 64 + 48 - BUTTON_HEIGHT - MARGIN * 2) + gui_text_box(error_rect, self.failed_reason, BODY_FONT_SIZE) + + button_width = (rect.width - BUTTON_SPACING - MARGIN * 2) / 2 + button_y = rect.height - BUTTON_HEIGHT - MARGIN + + if gui_button(rl.Rectangle(rect.x + MARGIN, button_y, button_width, BUTTON_HEIGHT), "Reboot device"): + HARDWARE.reboot() + + if gui_button(rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), "Start over", + button_style=ButtonStyle.PRIMARY): + self.state = SetupState.GETTING_STARTED + + def render_custom_url(self): + result = self.keyboard.render("Enter URL", "for Custom Software") + + # Enter pressed + if result == 1: + url = self.keyboard.text + self.keyboard.clear() + if url: + self.download(url) + + # Cancel pressed + elif result == 0: + self.state = SetupState.SOFTWARE_SELECTION + + def download(self, url: str): + # autocomplete incomplete URLs + if re.match("^([^/.]+)/([^/]+)$", url): + url = f"https://installer.comma.ai/{url}" + + self.download_url = url + self.state = SetupState.DOWNLOADING + + self.download_thread = threading.Thread(target=self._download_thread, daemon=True) + self.download_thread.start() + + def _download_thread(self): + try: + import tempfile + + _, tmpfile = tempfile.mkstemp(prefix="installer_") + + headers = {"User-Agent": USER_AGENT, "X-openpilot-serial": HARDWARE.get_serial()} + req = urllib.request.Request(self.download_url, headers=headers) + + with open(tmpfile, 'wb') as f, urllib.request.urlopen(req, timeout=30) as response: + total_size = int(response.headers.get('content-length', 0)) + downloaded = 0 + block_size = 8192 + + while True: + buffer = response.read(block_size) + if not buffer: + break + + downloaded += len(buffer) + f.write(buffer) + + if total_size: + self.download_progress = int(downloaded * 100 / total_size) + + is_elf = False + with open(tmpfile, 'rb') as f: + header = f.read(4) + is_elf = header == b'\x7fELF' + + if not is_elf: + self.download_failed(self.download_url, "No custom software found at this URL.") + return + + os.rename(tmpfile, "/tmp/installer") + os.chmod("/tmp/installer", 0o755) + + with open("/tmp/installer_url", "w") as f: + f.write(self.download_url) + + gui_app.request_close() + + except Exception: + error_msg = "Ensure the entered URL is valid, and the device's internet connection is good." + self.download_failed(self.download_url, error_msg) + + def download_failed(self, url: str, reason: str): + self.failed_url = url + self.failed_reason = reason + self.state = SetupState.DOWNLOAD_FAILED + + +def main(): + try: + gui_app.init_window("Setup") + setup = Setup() + for _ in gui_app.render(): + setup.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + setup.close() + except Exception as e: + print(f"Setup error: {e}") + finally: + gui_app.close() + + +if __name__ == "__main__": + main() From a6456503b092f636e4b9e9d730c297397c2a46b4 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 13:38:51 +0100 Subject: [PATCH 238/477] selfdrive/assets: add keyboard icons (#35247) --- selfdrive/assets/icons/arrow_right.png | 3 +++ selfdrive/assets/icons/arrow_right.svg | 3 +++ selfdrive/assets/icons/backspace.png | 3 +++ selfdrive/assets/icons/backspace.svg | 3 +++ selfdrive/assets/icons/shift.png | 3 +++ selfdrive/assets/icons/shift.svg | 3 +++ selfdrive/assets/icons/shift_lock.png | 3 +++ selfdrive/assets/icons/shift_lock.svg | 3 +++ 8 files changed, 24 insertions(+) create mode 100644 selfdrive/assets/icons/arrow_right.png create mode 100644 selfdrive/assets/icons/arrow_right.svg create mode 100644 selfdrive/assets/icons/backspace.png create mode 100644 selfdrive/assets/icons/backspace.svg create mode 100644 selfdrive/assets/icons/shift.png create mode 100644 selfdrive/assets/icons/shift.svg create mode 100644 selfdrive/assets/icons/shift_lock.png create mode 100644 selfdrive/assets/icons/shift_lock.svg diff --git a/selfdrive/assets/icons/arrow_right.png b/selfdrive/assets/icons/arrow_right.png new file mode 100644 index 0000000000..9897695399 --- /dev/null +++ b/selfdrive/assets/icons/arrow_right.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edfbafa7fe485b20d26382f198856c3ea798e57826aa8c4553c49a35aee005ed +size 883 diff --git a/selfdrive/assets/icons/arrow_right.svg b/selfdrive/assets/icons/arrow_right.svg new file mode 100644 index 0000000000..c2df1310a2 --- /dev/null +++ b/selfdrive/assets/icons/arrow_right.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb66f3b15fc670cb9ea2b17fc75984cc3fc4f0118b880ce555d3cc7672b72fa5 +size 583 diff --git a/selfdrive/assets/icons/backspace.png b/selfdrive/assets/icons/backspace.png new file mode 100644 index 0000000000..ece10a3d73 --- /dev/null +++ b/selfdrive/assets/icons/backspace.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36e16def9e727216e491cafbb3fe655f82797389b37d63946f1a9c030ecc6f96 +size 4470 diff --git a/selfdrive/assets/icons/backspace.svg b/selfdrive/assets/icons/backspace.svg new file mode 100644 index 0000000000..8da38a106a --- /dev/null +++ b/selfdrive/assets/icons/backspace.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10d92681ea39067298851a1d298959fbd1904e63431fbbcfff953f22a65b61c3 +size 468 diff --git a/selfdrive/assets/icons/shift.png b/selfdrive/assets/icons/shift.png new file mode 100644 index 0000000000..7026a807e7 --- /dev/null +++ b/selfdrive/assets/icons/shift.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25abdab506c6ae81b0a379d86378e0ad73f6fa093be5dc81ed1f192e5b146044 +size 3113 diff --git a/selfdrive/assets/icons/shift.svg b/selfdrive/assets/icons/shift.svg new file mode 100644 index 0000000000..7f890856bd --- /dev/null +++ b/selfdrive/assets/icons/shift.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30f7cd752143d0d9148263d479d5c38809c9ecd64a0bfb5f0fe6d393faedcaa7 +size 232 diff --git a/selfdrive/assets/icons/shift_lock.png b/selfdrive/assets/icons/shift_lock.png new file mode 100644 index 0000000000..1a0464ebcd --- /dev/null +++ b/selfdrive/assets/icons/shift_lock.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0796cb759559f1608a7498c2a00ca48e4d398e4a7b927778db0ae3f00b5c5bcb +size 3092 diff --git a/selfdrive/assets/icons/shift_lock.svg b/selfdrive/assets/icons/shift_lock.svg new file mode 100644 index 0000000000..388e722b49 --- /dev/null +++ b/selfdrive/assets/icons/shift_lock.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9eeae279ca4022f109e8a1873def65edfc86108c5315e0af98e7f413db75218 +size 1074 From dcfbb99b1358582aab63e82c903d3229008e4673 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 16 May 2025 20:50:27 +0800 Subject: [PATCH 239/477] system/ui: add password visibility toggle with eye icon to keyboard (#35246) * add password visibility toggle with eye icon to keyboard * added show_password_toggle --- system/ui/lib/inputbox.py | 7 +++-- system/ui/widgets/keyboard.py | 51 ++++++++++++++++++++++++++++++----- system/ui/widgets/network.py | 2 +- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index d933d42476..8e53446b43 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -62,13 +62,12 @@ class InputBox: return True return False - def render(self, rect, color=rl.LIGHTGRAY, border_color=rl.DARKGRAY, text_color=rl.BLACK, font_size=80): + def render(self, rect, color=rl.BLACK, border_color=rl.DARKGRAY, text_color=rl.WHITE, font_size=80): # Handle mouse input self._handle_mouse_input(rect, font_size) # Draw input box rl.draw_rectangle_rec(rect, color) - rl.draw_rectangle_lines_ex(rect, 1, border_color) # Process keyboard input self._handle_keyboard_input() @@ -81,7 +80,7 @@ class InputBox: # Display text font = gui_app.font() - display_text = "•" * len(self._input_text) if self._password_mode else self._input_text + display_text = "*" * len(self._input_text) if self._password_mode else self._input_text padding = 10 rl.draw_text_ex( font, @@ -100,7 +99,7 @@ class InputBox: cursor_height = font_size + 4 cursor_y = rect.y + rect.height / 2 - cursor_height / 2 - rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.BLACK) + rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.LIGHTGRAY) def _handle_mouse_input(self, rect, font_size): """Handle mouse clicks to position cursor.""" diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index b626a26b67..37580f80e5 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,5 +1,5 @@ import pyray as rl -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label @@ -45,11 +45,16 @@ keyboard_layouts = { class Keyboard: - def __init__(self, max_text_size: int = 255, min_text_size: int = 0): + def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False): self._layout = keyboard_layouts["lowercase"] self._max_text_size = max_text_size self._min_text_size = min_text_size self._input_box = InputBox(max_text_size) + self._password_mode = password_mode + self._show_password_toggle = show_password_toggle + + self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) + self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) @property def text(self): @@ -60,14 +65,17 @@ class Keyboard: def render(self, title: str, sub_title: str): rect = rl.Rectangle(CONTENT_MARGIN, CONTENT_MARGIN, gui_app.width - 2 * CONTENT_MARGIN, gui_app.height - 2 * CONTENT_MARGIN) - gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90) - gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, rl.GRAY) - if gui_button(rl.Rectangle(rect.x + rect.width - 300, rect.y, 300, 100), "Cancel"): + gui_label(rl.Rectangle(rect.x, rect.y, rect.width, 95), title, 90, font_weight=FontWeight.BOLD) + gui_label(rl.Rectangle(rect.x, rect.y + 95, rect.width, 60), sub_title, 55, font_weight=FontWeight.NORMAL) + if gui_button(rl.Rectangle(rect.x + rect.width - 386, rect.y, 386, 125), "Cancel"): self.clear() return 0 - # Text box for input - self._input_box.render(rl.Rectangle(rect.x, rect.y + 160, rect.width, 100)) + # Draw input box and password toggle + input_margin = 25 + input_box_rect = rl.Rectangle(rect.x + input_margin, rect.y + 160, rect.width - input_margin, 100) + self._render_input_area(input_box_rect) + h_space, v_space = 15, 15 row_y_start = rect.y + 300 # Starting Y position for the first row key_height = (rect.height - 300 - 3 * v_space) / 4 @@ -95,6 +103,35 @@ class Keyboard: return -1 + def _render_input_area(self, input_rect: rl.Rectangle): + if self._show_password_toggle: + self._input_box.set_password_mode(self._password_mode) + self._input_box.render(rl.Rectangle(input_rect.x, input_rect.y, input_rect.width - 100, input_rect.height)) + + # render eye icon + eye_texture = self._eye_closed_texture if self._password_mode else self._eye_open_texture + + eye_rect = rl.Rectangle(input_rect.x + input_rect.width - 90, input_rect.y, 80, input_rect.height) + eye_x = eye_rect.x + (eye_rect.width - eye_texture.width) / 2 + eye_y = eye_rect.y + (eye_rect.height - eye_texture.height) / 2 + + rl.draw_texture_v(eye_texture, rl.Vector2(eye_x, eye_y), rl.WHITE) + + # Handle click on eye icon + if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT) and rl.check_collision_point_rec( + rl.get_mouse_position(), eye_rect + ): + self._password_mode = not self._password_mode + else: + self._input_box.render(input_rect) + + rl.draw_line_ex( + rl.Vector2(input_rect.x, input_rect.y + input_rect.height - 2), + rl.Vector2(input_rect.x + input_rect.width, input_rect.y + input_rect.height - 2), + 3.0, # 3 pixel thickness + rl.Color(189, 189, 189, 255), + ) + def handle_key_press(self, key): if key in (SHIFT_DOWN_KEY, ABC_KEY): self._layout = keyboard_layouts["lowercase"] diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index d34ad44f55..5b9d05c96e 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -48,7 +48,7 @@ class WifiManagerUI: self.state: UIState = StateIdle() self.btn_width = 200 self.scroll_panel = GuiScrollPanel() - self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH) + self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) self._networks: list[NetworkInfo] = [] From b740d23dd02de3e314d6b4e4c7a6803ceddaba13 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 16 May 2025 20:51:08 +0800 Subject: [PATCH 240/477] system/ui: display icons in wifi manager (#35244) display icons in wifi manager --- system/ui/widgets/network.py | 66 ++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 5b9d05c96e..29d9a499da 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -2,11 +2,11 @@ from dataclasses import dataclass from typing import Literal import pyray as rl -from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.button import gui_button from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType from openpilot.system.ui.widgets.keyboard import Keyboard from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog @@ -14,7 +14,14 @@ NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 MAX_PASSWORD_LENGTH = 64 ITEM_HEIGHT = 160 +ICON_SIZE = 49 +STRENGTH_ICONS = [ + "icons/wifi_strength_low.png", + "icons/wifi_strength_medium.png", + "icons/wifi_strength_high.png", + "icons/wifi_strength_full.png", +] @dataclass class StateIdle: @@ -109,14 +116,14 @@ class WifiManagerUI: rl.end_scissor_mode() def _draw_network_item(self, rect, network: NetworkInfo, clicked: bool): - label_rect = rl.Rectangle(rect.x, rect.y, rect.width - self.btn_width * 2, ITEM_HEIGHT) - state_rect = rl.Rectangle(rect.x + rect.width - self.btn_width * 2 - 150, rect.y, 300, ITEM_HEIGHT) + spacing = 50 + ssid_rect = rl.Rectangle(rect.x, rect.y, rect.width - self.btn_width * 2, ITEM_HEIGHT) + signal_icon_rect = rl.Rectangle(rect.x + rect.width - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) + security_icon_rect = rl.Rectangle(signal_icon_rect.x - spacing - ICON_SIZE, rect.y + (ITEM_HEIGHT - ICON_SIZE) / 2, ICON_SIZE, ICON_SIZE) - gui_label(label_rect, network.ssid, 55) + gui_label(ssid_rect, network.ssid, 55) status_text = "" - if network.is_connected: - status_text = "Connected" match self.state: case StateConnecting(network=connecting): if connecting.ssid == network.ssid: @@ -124,26 +131,49 @@ class WifiManagerUI: case StateForgetting(network=forgetting): if forgetting.ssid == network.ssid: status_text = "FORGETTING..." + if status_text: - rl.gui_label(state_rect, status_text) + status_text_rect = rl.Rectangle(security_icon_rect.x - 410, rect.y, 410, ITEM_HEIGHT) + rl.gui_label(status_text_rect, status_text) + else: + # If the network is saved, show the "Forget" button + if network.is_saved: + forget_btn_rect = rl.Rectangle(security_icon_rect.x - self.btn_width - spacing, + rect.y + (ITEM_HEIGHT - 80) / 2, + self.btn_width, + 80, + ) + if isinstance(self.state, StateIdle) and gui_button(forget_btn_rect, "Forget") and clicked: + self.state = StateShowForgetConfirm(network) - # If the network is saved, show the "Forget" button - if network.is_saved: - forget_btn_rect = rl.Rectangle( - rect.x + rect.width - self.btn_width, - rect.y + (ITEM_HEIGHT - 80) / 2, - self.btn_width, - 80, - ) - if isinstance(self.state, StateIdle) and gui_button(forget_btn_rect, "Forget") and clicked: - self.state = StateShowForgetConfirm(network) + self._draw_status_icon(security_icon_rect, network) + self._draw_signal_strength_icon(signal_icon_rect, network) - if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), label_rect) and clicked: + if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), ssid_rect) and clicked: if not network.is_saved: self.state = StateNeedsAuth(network) else: self.connect_to_network(network) + def _draw_status_icon(self, rect, network: NetworkInfo): + """Draw the status icon based on network's connection state""" + icon_file = "" + if network.is_connected: + icon_file = "icons/checkmark.png" + elif network.security_type == SecurityType.UNSUPPORTED: + icon_file = "icons/circled_slash.png" + else: + icon_file = "icons/lock_closed.png" + + texture = gui_app.texture(icon_file, ICON_SIZE, ICON_SIZE) + icon_rect = rl.Vector2(rect.x, rect.y + (ICON_SIZE - texture.height) / 2) + rl.draw_texture_v(texture, icon_rect, rl.WHITE) + + def _draw_signal_strength_icon(self, rect: rl.Rectangle, network: NetworkInfo): + """Draw the Wi-Fi signal strength icon based on network's signal strength""" + strength_level = max(0, min(3, round(network.strength / 33.0))) + rl.draw_texture_v(gui_app.texture(STRENGTH_ICONS[strength_level], ICON_SIZE, ICON_SIZE), rl.Vector2(rect.x, rect.y), rl.WHITE) + def connect_to_network(self, network: NetworkInfo, password=''): self.state = StateConnecting(network) if network.is_saved and not password: From 6adc7e6bdb318a178595a6532a21da8d79a97cb0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 17 May 2025 02:10:58 +0800 Subject: [PATCH 241/477] system/ui: disable continue button when no network connecton in setup.py (#35249) disable continue button when no network connecton --- system/ui/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/system/ui/setup.py b/system/ui/setup.py index 8c75b8e7c7..7cf4cac9b4 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -167,6 +167,7 @@ class Setup: rl.Rectangle(rect.x + MARGIN + button_width + BUTTON_SPACING, button_y, button_width, BUTTON_HEIGHT), continue_text, button_style=ButtonStyle.PRIMARY if continue_enabled else ButtonStyle.NORMAL, + is_enabled=continue_enabled, ): self.state = SetupState.SOFTWARE_SELECTION self.stop_network_check_thread.set() From e9680a40bf2186545f05d9431d9ac440f92329f7 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 19:17:24 +0100 Subject: [PATCH 242/477] selfdrive/assets: generate keyboard icons from bootstrap-icons (#35250) * Revert "selfdrive/assets: add keyboard icons (#35247)" This reverts commit a6456503b092f636e4b9e9d730c297397c2a46b4. * selfdrive/assets: use bootstrap icons, generate keyboard icons * cleanup * switch shift-fill for capslock --- selfdrive/assets/icons/arrow-down.png | 3 +++ selfdrive/assets/icons/arrow-right.png | 3 +++ selfdrive/assets/icons/arrow_right.png | 3 --- selfdrive/assets/icons/arrow_right.svg | 3 --- selfdrive/assets/icons/backspace.png | 4 ++-- selfdrive/assets/icons/backspace.svg | 3 --- selfdrive/assets/icons/capslock.png | 3 +++ selfdrive/assets/icons/shift.png | 4 ++-- selfdrive/assets/icons/shift.svg | 3 --- selfdrive/assets/icons/shift_lock.png | 3 --- selfdrive/assets/icons/shift_lock.svg | 3 --- selfdrive/assets/prep-svg.sh | 25 ++++++++++++++++++++++++- 12 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 selfdrive/assets/icons/arrow-down.png create mode 100644 selfdrive/assets/icons/arrow-right.png delete mode 100644 selfdrive/assets/icons/arrow_right.png delete mode 100644 selfdrive/assets/icons/arrow_right.svg delete mode 100644 selfdrive/assets/icons/backspace.svg create mode 100644 selfdrive/assets/icons/capslock.png delete mode 100644 selfdrive/assets/icons/shift.svg delete mode 100644 selfdrive/assets/icons/shift_lock.png delete mode 100644 selfdrive/assets/icons/shift_lock.svg diff --git a/selfdrive/assets/icons/arrow-down.png b/selfdrive/assets/icons/arrow-down.png new file mode 100644 index 0000000000..834c1cb8b9 --- /dev/null +++ b/selfdrive/assets/icons/arrow-down.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e12a3cf36fdef107d237457e20b44e16320414f9de8a1791aff1ec5fd85ccef +size 2390 diff --git a/selfdrive/assets/icons/arrow-right.png b/selfdrive/assets/icons/arrow-right.png new file mode 100644 index 0000000000..b275134f64 --- /dev/null +++ b/selfdrive/assets/icons/arrow-right.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a999d5f3e616eeafc310689accdd26efb90769596604a62c460ef8acece18bc +size 1734 diff --git a/selfdrive/assets/icons/arrow_right.png b/selfdrive/assets/icons/arrow_right.png deleted file mode 100644 index 9897695399..0000000000 --- a/selfdrive/assets/icons/arrow_right.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:edfbafa7fe485b20d26382f198856c3ea798e57826aa8c4553c49a35aee005ed -size 883 diff --git a/selfdrive/assets/icons/arrow_right.svg b/selfdrive/assets/icons/arrow_right.svg deleted file mode 100644 index c2df1310a2..0000000000 --- a/selfdrive/assets/icons/arrow_right.svg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb66f3b15fc670cb9ea2b17fc75984cc3fc4f0118b880ce555d3cc7672b72fa5 -size 583 diff --git a/selfdrive/assets/icons/backspace.png b/selfdrive/assets/icons/backspace.png index ece10a3d73..16686be6f5 100644 --- a/selfdrive/assets/icons/backspace.png +++ b/selfdrive/assets/icons/backspace.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36e16def9e727216e491cafbb3fe655f82797389b37d63946f1a9c030ecc6f96 -size 4470 +oid sha256:576da562df8eb513e64ccb614ec727b257cbca8b5507974d01efc0f64c5382c2 +size 6267 diff --git a/selfdrive/assets/icons/backspace.svg b/selfdrive/assets/icons/backspace.svg deleted file mode 100644 index 8da38a106a..0000000000 --- a/selfdrive/assets/icons/backspace.svg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:10d92681ea39067298851a1d298959fbd1904e63431fbbcfff953f22a65b61c3 -size 468 diff --git a/selfdrive/assets/icons/capslock.png b/selfdrive/assets/icons/capslock.png new file mode 100644 index 0000000000..61414d7230 --- /dev/null +++ b/selfdrive/assets/icons/capslock.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6621d000d9514df1d636e9953941eadec415ff950724f26a61e0fcbaa7a3818 +size 5403 diff --git a/selfdrive/assets/icons/shift.png b/selfdrive/assets/icons/shift.png index 7026a807e7..de2a68b482 100644 --- a/selfdrive/assets/icons/shift.png +++ b/selfdrive/assets/icons/shift.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25abdab506c6ae81b0a379d86378e0ad73f6fa093be5dc81ed1f192e5b146044 -size 3113 +oid sha256:a93dd816bd0600ad47d10ebe530326bfa725dc31e4d5e1ee275f39b10f17a59d +size 4931 diff --git a/selfdrive/assets/icons/shift.svg b/selfdrive/assets/icons/shift.svg deleted file mode 100644 index 7f890856bd..0000000000 --- a/selfdrive/assets/icons/shift.svg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:30f7cd752143d0d9148263d479d5c38809c9ecd64a0bfb5f0fe6d393faedcaa7 -size 232 diff --git a/selfdrive/assets/icons/shift_lock.png b/selfdrive/assets/icons/shift_lock.png deleted file mode 100644 index 1a0464ebcd..0000000000 --- a/selfdrive/assets/icons/shift_lock.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0796cb759559f1608a7498c2a00ca48e4d398e4a7b927778db0ae3f00b5c5bcb -size 3092 diff --git a/selfdrive/assets/icons/shift_lock.svg b/selfdrive/assets/icons/shift_lock.svg deleted file mode 100644 index 388e722b49..0000000000 --- a/selfdrive/assets/icons/shift_lock.svg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e9eeae279ca4022f109e8a1873def65edfc86108c5315e0af98e7f413db75218 -size 1074 diff --git a/selfdrive/assets/prep-svg.sh b/selfdrive/assets/prep-svg.sh index 661464a5c6..d29ce3025d 100755 --- a/selfdrive/assets/prep-svg.sh +++ b/selfdrive/assets/prep-svg.sh @@ -2,6 +2,24 @@ set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +ICONS_DIR="$DIR/icons" +BOOTSTRAP_SVG="$DIR/../../third_party/bootstrap/bootstrap-icons.svg" + +ICON_IDS=( + arrow-down + arrow-right + backspace + capslock + shift +) +ICON_FILL_COLOR="#fff" + +# extract bootstrap icons +for id in "${ICON_IDS[@]}"; do + svg="${ICONS_DIR}/${id}.svg" + perl -0777 -ne "print \$& if /]*id=\"$id\"[^>]*>.*?<\/symbol>/s" "$BOOTSTRAP_SVG" \ + | sed "s//<\/svg>/" > "$svg" +done # sudo apt install inkscape @@ -17,7 +35,12 @@ for svg in $(find $DIR -type f | grep svg$); do else export_dim="--export-height=512" fi - inkscape "$svg" --export-filename="$png" $export_dim + inkscape "$svg" --export-filename="$png" "$export_dim" optipng -o7 -strip all "$png" done + +# cleanup bootstrap SVGs +for id in "${ICON_IDS[@]}"; do + rm "${ICONS_DIR}/${id}.svg" +done From 5babe18184606b81b5829d028f28d7b18edc24e5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 17 May 2025 02:24:04 +0800 Subject: [PATCH 243/477] system/ui/setup: fix WiFi manager UI element overlap issues (#35251) fix ui overlap issue --- system/ui/setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system/ui/setup.py b/system/ui/setup.py index 7cf4cac9b4..2e40610395 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -144,6 +144,10 @@ class Setup: self.network_check_thread.join() def render_network_setup(self, rect: rl.Rectangle): + if self.wifi_ui.require_full_screen: + self.wifi_ui.render(rect) + return + title_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - MARGIN * 2, TITLE_FONT_SIZE) gui_label(title_rect, "Connect to Wi-Fi", TITLE_FONT_SIZE, font_weight=FontWeight.MEDIUM) From 71e3fd718342bfac57ee05c1e81518faf81aad94 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 17 May 2025 02:43:03 +0800 Subject: [PATCH 244/477] system/ui: use icon for special characters (#35248) * use icon for special characters * add icon for SHIFT_DOWN_KEY --- system/ui/lib/button.py | 44 +++++++++++++++++++++++++++-------- system/ui/widgets/keyboard.py | 14 ++++++++++- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index dfe75d88bd..2d58aee640 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -16,6 +16,7 @@ class TextAlignment(IntEnum): RIGHT = 2 +ICON_PADDING = 15 DEFAULT_BUTTON_FONT_SIZE = 60 BUTTON_ENABLED_TEXT_COLOR = rl.Color(228, 228, 228, 255) BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) @@ -46,6 +47,7 @@ def gui_button( border_radius: int = 10, # Corner rounding in pixels text_alignment: TextAlignment = TextAlignment.CENTER, text_padding: int = 20, # Padding for left/right alignment + icon = None, ) -> int: result = 0 @@ -68,20 +70,42 @@ def gui_button( rl.draw_rectangle_rounded(rect, roundness, 20, rl.BLACK) rl.draw_rectangle_rounded_lines_ex(rect, roundness, 20, 2, rl.WHITE) + # Handle icon and text positioning font = gui_app.font(font_weight) text_size = rl.measure_text_ex(font, text, font_size, 0) text_pos = rl.Vector2(0, rect.y + (rect.height - text_size.y) // 2) # Vertical centering - # Horizontal alignment - if text_alignment == TextAlignment.LEFT: - text_pos.x = rect.x + text_padding - elif text_alignment == TextAlignment.CENTER: - text_pos.x = rect.x + (rect.width - text_size.x) // 2 - elif text_alignment == TextAlignment.RIGHT: - text_pos.x = rect.x + rect.width - text_size.x - text_padding + # Draw icon if provided + if icon: + icon_y = rect.y + (rect.height - icon.height) / 2 + if text: + if text_alignment == TextAlignment.LEFT: + icon_x = rect.x + text_padding + text_pos.x = icon_x + icon.width + ICON_PADDING + elif text_alignment == TextAlignment.CENTER: + total_width = icon.width + ICON_PADDING + text_size.x + icon_x = rect.x + (rect.width - total_width) / 2 + text_pos.x = icon_x + icon.width + ICON_PADDING + else: # RIGHT + text_pos.x = rect.x + rect.width - text_size.x - text_padding + icon_x = text_pos.x - ICON_PADDING - icon.width + else: + # Center icon when no text + icon_x = rect.x + (rect.width - icon.width) / 2 - # Draw the button text - text_color = BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR - rl.draw_text_ex(font, text, text_pos, font_size, 0, text_color) + rl.draw_texture_v(icon, rl.Vector2(icon_x, icon_y), rl.WHITE if is_enabled else rl.Color(255, 255, 255, 100)) + else: + # No icon, position text normally + if text_alignment == TextAlignment.LEFT: + text_pos.x = rect.x + text_padding + elif text_alignment == TextAlignment.CENTER: + text_pos.x = rect.x + (rect.width - text_size.x) // 2 + elif text_alignment == TextAlignment.RIGHT: + text_pos.x = rect.x + rect.width - text_size.x - text_padding + + # Draw the button text if any + if text: + text_color = BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR + rl.draw_text_ex(font, text, text_pos, font_size, 0, text_color) return result diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 37580f80e5..aabd664171 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -55,6 +55,11 @@ class Keyboard: self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) + self._key_icons = { + BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 60, 60), + SHIFT_KEY: gui_app.texture("icons/shift.png", 60, 60), + SHIFT_DOWN_KEY: gui_app.texture("icons/arrow-down.png", 60, 60), + } @property def text(self): @@ -95,7 +100,14 @@ class Keyboard: start_x += new_width is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size - if gui_button(key_rect, key, is_enabled=is_enabled): + result = -1 + if key in self._key_icons: + texture = self._key_icons[key] + result = gui_button(key_rect, "", icon=texture, is_enabled=is_enabled) + else: + result = gui_button(key_rect, key, is_enabled=is_enabled) + + if result: if key == ENTER_KEY: return 1 else: From 09fde3c3ad2f15d27bbe47c0521ce7dd0f0bf0c0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 17 May 2025 04:56:41 +0800 Subject: [PATCH 245/477] system/ui: Use OPENPILOT_URL instead of google.com for network connectivity check (#35254) check OPENPILOT_URL --- system/ui/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/setup.py b/system/ui/setup.py index 2e40610395..0ec5d6395e 100755 --- a/system/ui/setup.py +++ b/system/ui/setup.py @@ -123,7 +123,7 @@ class Setup: while not self.stop_network_check_thread.is_set(): if self.state == SetupState.NETWORK_SETUP: try: - urllib.request.urlopen("https://google.com", timeout=2) + urllib.request.urlopen(OPENPILOT_URL, timeout=2) self.network_connected.set() if HARDWARE.get_network_type() == NetworkType.wifi: self.wifi_connected.set() From 8097a92515c0273a4ef0ef7b910093a7a8772da4 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Fri, 16 May 2025 22:24:03 +0100 Subject: [PATCH 246/477] zipapp pack (#35253) Used to ship python UI in agnos without an openpilot clone * add a main method to target * pack script * validate inputs * refactors * copy into temp, dont keep this * cleanup * help messages * rename to pack.py * pack.py * updates for device * moar * don't use cereal * just log normally * use importlib.resources * revert * Revert "don't use cereal" This reverts commit 7208524d422d88a1b07e209359aeb25e8b3bf4e7. * fix cereal? * cleanup * Revert "cleanup" This reverts commit 921edfe5020f244dbdf4f26767af7c98ca837d1c. * cython hotfix * Reapply "cleanup" This reverts commit 9b54552f784dea1b1eb4ffc03937571e4fc851ba. * more cleanup * any script? * slightly clearer * rm print * nothing python should use SVGs --------- Co-authored-by: Trey Moen --- cereal/__init__.py | 10 ++++--- release/pack.py | 50 +++++++++++++++++++++++++++++++++++ system/ui/lib/application.py | 16 ++++++----- system/ui/lib/wifi_manager.py | 13 ++++++--- system/ui/spinner.py | 6 ++++- 5 files changed, 79 insertions(+), 16 deletions(-) create mode 100755 release/pack.py diff --git a/cereal/__init__.py b/cereal/__init__.py index 89c5cf38e3..93f4d77227 100644 --- a/cereal/__init__.py +++ b/cereal/__init__.py @@ -1,9 +1,11 @@ import os import capnp +from importlib.resources import as_file, files -CEREAL_PATH = os.path.dirname(os.path.abspath(__file__)) capnp.remove_import_hook() -log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp")) -car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp")) -custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp")) +with as_file(files("cereal")) as fspath: + CEREAL_PATH = fspath.as_posix() + log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp")) + car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp")) + custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp")) diff --git a/release/pack.py b/release/pack.py new file mode 100755 index 0000000000..1cb1a47a48 --- /dev/null +++ b/release/pack.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import importlib +import shutil +import sys +import tempfile +import zipapp +from argparse import ArgumentParser +from pathlib import Path + +from openpilot.common.basedir import BASEDIR + + +DIRS = ['cereal', 'openpilot'] +EXTS = ['.png', '.py', '.ttf', '.capnp'] +INTERPRETER = '/usr/bin/env python3' + + +def copy(src, dest): + if any(src.endswith(ext) for ext in EXTS): + shutil.copy2(src, dest, follow_symlinks=True) + + +if __name__ == '__main__': + parser = ArgumentParser(prog='pack.py', description="package script into a portable executable", epilog='comma.ai') + parser.add_argument('-e', '--entrypoint', help="function to call in module, default is 'main'", default='main') + parser.add_argument('-o', '--output', help='output file') + parser.add_argument('module', help="the module to target, e.g. 'openpilot.system.ui.spinner'") + args = parser.parse_args() + + if not args.output: + args.output = args.module + + try: + mod = importlib.import_module(args.module) + except ModuleNotFoundError: + print(f'{args.module} not found, typo?') + sys.exit(1) + + if not hasattr(mod, args.entrypoint): + print(f'{args.module} does not have a {args.entrypoint}() function, typo?') + sys.exit(1) + + with tempfile.TemporaryDirectory() as tmp: + for directory in DIRS: + shutil.copytree(BASEDIR + '/' + directory, tmp + '/' + directory, symlinks=False, dirs_exist_ok=True, copy_function=copy) + entry = f'{args.module}:{args.entrypoint}' + zipapp.create_archive(tmp, target=args.output, interpreter=INTERPRETER, main=entry) + + print(f'created executable {Path(args.output).resolve()}') diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 9e6c8ef28d..467cd8c5a8 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -3,7 +3,7 @@ import os import time import pyray as rl from enum import IntEnum -from openpilot.common.basedir import BASEDIR +from importlib.resources import as_file, files from openpilot.common.swaglog import cloudlog from openpilot.system.hardware import HARDWARE @@ -18,9 +18,9 @@ STRICT_MODE = os.getenv("STRICT_MODE") == '1' DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE -ASSETS_DIR = os.path.join(BASEDIR, "selfdrive/assets") -FONT_DIR = os.path.join(BASEDIR, "selfdrive/assets/fonts") +ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") +FONT_DIR = ASSETS_DIR.joinpath("fonts") class FontWeight(IntEnum): BLACK = 0 @@ -74,7 +74,8 @@ class GuiApplication: if cache_key in self._textures: return self._textures[cache_key] - texture_obj = self._load_texture_from_image(os.path.join(ASSETS_DIR, asset_path), width, height, alpha_premultiply, keep_aspect_ratio) + with as_file(ASSETS_DIR.joinpath(asset_path)) as fspath: + texture_obj = self._load_texture_from_image(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio) self._textures[cache_key] = texture_obj return texture_obj @@ -163,9 +164,10 @@ class GuiApplication: ) for index, font_file in enumerate(font_files): - font = rl.load_font_ex(os.path.join(FONT_DIR, font_file), 120, None, 0) - rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) - self._fonts[index] = font + with as_file(FONT_DIR.joinpath(font_file)) as fspath: + font = rl.load_font_ex(fspath.as_posix(), 120, None, 0) + rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) + self._fonts[index] = font rl.gui_set_font(self._fonts[FontWeight.NORMAL]) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 7070266aab..4ac7466cb7 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -13,7 +13,11 @@ from dbus_next.aio import MessageBus from dbus_next import BusType, Variant, Message from dbus_next.errors import DBusError from dbus_next.constants import MessageType -from openpilot.common.params import Params +try: + from openpilot.common.params import Params +except ImportError: + # Params/Cythonized modules are not available in zipapp + Params = None from openpilot.common.swaglog import cloudlog T = TypeVar("T") @@ -81,9 +85,10 @@ class WifiManager: self.scan_task: asyncio.Task | None = None # Set tethering ssid as "weedle" + first 4 characters of a dongle id self._tethering_ssid = "weedle" - dongle_id = Params().get("DongleId", encoding="utf-8") - if dongle_id: - self._tethering_ssid += "-" + dongle_id[:4] + if Params is not None: + dongle_id = Params().get("DongleId", encoding="utf-8") + if dongle_id: + self._tethering_ssid += "-" + dongle_id[:4] self.running: bool = True self._current_connection_ssid: str | None = None diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 5fd3f964a6..2af24c4e51 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -98,7 +98,11 @@ class Spinner(BaseWindow[SpinnerRenderer]): self.update(str(round(100 * cur / total))) -if __name__ == "__main__": +def main(): with Spinner() as s: s.update("Spinner text") time.sleep(5) + + +if __name__ == "__main__": + main() From da670108e7f3f9bb391b3067330e957fd33300bc Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Sat, 17 May 2025 00:23:55 +0100 Subject: [PATCH 247/477] system/ui: all font weights (#35255) --- system/ui/lib/application.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 467cd8c5a8..a2ea392784 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -23,14 +23,15 @@ ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") class FontWeight(IntEnum): - BLACK = 0 - BOLD = 1 - EXTRA_BOLD = 2 - EXTRA_LIGHT = 3 + THIN = 0 + EXTRA_LIGHT = 1 + LIGHT = 2 + NORMAL = 3 MEDIUM = 4 - NORMAL = 5 - SEMI_BOLD = 6 - THIN = 7 + SEMI_BOLD = 5 + BOLD = 6 + EXTRA_BOLD = 7 + BLACK = 8 class GuiApplication: @@ -153,14 +154,15 @@ class GuiApplication: def _load_fonts(self): font_files = ( - "Inter-Black.ttf", + "Inter-Thin.ttf", + "Inter-ExtraLight.ttf", + "Inter-Light.ttf", + "Inter-Regular.ttf", + "Inter-Medium.ttf", + "Inter-SemiBold.ttf", "Inter-Bold.ttf", "Inter-ExtraBold.ttf", - "Inter-ExtraLight.ttf", - "Inter-Medium.ttf", - "Inter-Regular.ttf", - "Inter-SemiBold.ttf", - "Inter-Thin.ttf" + "Inter-Black.ttf", ) for index, font_file in enumerate(font_files): From 49c422e8723f12a14f68a10fcd7154a7c03fe3c8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 16 May 2025 21:29:16 -0700 Subject: [PATCH 248/477] Tesla: fix angle control saturation (#35256) * we shouldn't need this * or this? * stash junk * hmm * comment * cc * sadly we have to maintain behavior * bump --- selfdrive/controls/lib/latcontrol_angle.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/latcontrol_angle.py b/selfdrive/controls/lib/latcontrol_angle.py index 1b249a3d1a..f94dca57c1 100644 --- a/selfdrive/controls/lib/latcontrol_angle.py +++ b/selfdrive/controls/lib/latcontrol_angle.py @@ -10,6 +10,7 @@ class LatControlAngle(LatControl): def __init__(self, CP, CI): super().__init__(CP, CI) self.sat_check_min_speed = 5. + self.use_steer_limited_by_controls = CP.brand == "tesla" def update(self, active, CS, VM, params, steer_limited_by_controls, desired_curvature, calibrated_pose, curvature_limited): angle_log = log.ControlsState.LateralAngleState.new_message() @@ -22,7 +23,13 @@ class LatControlAngle(LatControl): angle_steers_des = math.degrees(VM.get_steer_from_curvature(-desired_curvature, CS.vEgo, params.roll)) angle_steers_des += params.angleOffsetDeg - angle_control_saturated = abs(angle_steers_des - CS.steeringAngleDeg) > STEER_ANGLE_SATURATION_THRESHOLD + if self.use_steer_limited_by_controls: + # these cars' carcontrolers calculate max lateral accel and jerk, so we can rely on carOutput for saturation + angle_control_saturated = steer_limited_by_controls + else: + # for cars which use a method of limiting torque such as a torque signal (Nissan and Toyota) + # or relying on EPS (Ford Q3), carOutput does not capture maxing out torque # TODO: this can be improved + angle_control_saturated = abs(angle_steers_des - CS.steeringAngleDeg) > STEER_ANGLE_SATURATION_THRESHOLD angle_log.saturated = bool(self._check_saturation(angle_control_saturated, CS, False, curvature_limited)) angle_log.steeringAngleDeg = float(CS.steeringAngleDeg) angle_log.steeringAngleDesiredDeg = angle_steers_des From 52dc4141c5e5ec3b4809ad12bb6eb10731a6e5a5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 17 May 2025 10:09:50 -0400 Subject: [PATCH 249/477] MADS: evaluate brakePressed and regenBraking for Pause Lateral mode (#926) * simplify * MADS: evaluate brakePressed and regenBraking for Pause Lateral mode * nah --- sunnypilot/mads/mads.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 9aeb67972f..1132a0df2f 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -9,6 +9,7 @@ from cereal import log, custom from opendbc.car import structs from opendbc.car.hyundai.values import HyundaiFlags +from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState @@ -24,6 +25,8 @@ IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput) class ModularAssistiveDrivingSystem: def __init__(self, selfdrive): + self.CP = selfdrive.CP + self.CS_prev = selfdrive.CS_prev self.params = selfdrive.params self.enabled = False @@ -35,9 +38,10 @@ class ModularAssistiveDrivingSystem: self.state_machine = StateMachine(self) self.events = self.selfdrive.events self.events_sp = self.selfdrive.events_sp + self.disengage_on_accelerator = not self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS - if self.selfdrive.CP.brand == "hyundai": - if self.selfdrive.CP.flags & (HyundaiFlags.HAS_LDA_BUTTON | HyundaiFlags.CANFD): + if self.CP.brand == "hyundai": + if self.CP.flags & (HyundaiFlags.HAS_LDA_BUTTON | HyundaiFlags.CANFD): self.allow_always = True # read params on init @@ -50,6 +54,21 @@ class ModularAssistiveDrivingSystem: self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed") self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode") + def pedal_pressed_non_gas_pressed(self, CS: structs.CarState) -> bool: + if self.events.has(EventName.pedalPressed) and not (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator): + return True + + return False + + def should_silent_lkas_enable(self, CS: structs.CarState) -> bool: + if self.pause_lateral_on_brake_toggle and self.pedal_pressed_non_gas_pressed(CS): + return False + + if self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT): + return False + + return True + def update_events(self, CS: structs.CarState): def update_unified_engagement_mode(): uem_blocked = self.enabled or (self.selfdrive.enabled and self.selfdrive.enabled_prev) @@ -86,7 +105,7 @@ class ModularAssistiveDrivingSystem: transition_paused_state() if self.pause_lateral_on_brake_toggle: - if CS.brakePressed: + if self.pedal_pressed_non_gas_pressed(CS): transition_paused_state() self.events.remove(EventName.preEnableStandstill) @@ -99,7 +118,7 @@ class ModularAssistiveDrivingSystem: update_unified_engagement_mode() else: if self.main_enabled_toggle: - if CS.cruiseState.available and not self.selfdrive.CS_prev.cruiseState.available: + if CS.cruiseState.available and not self.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasEnable) for be in CS.buttonEvents: @@ -117,11 +136,10 @@ class ModularAssistiveDrivingSystem: if not CS.cruiseState.available: self.events.remove(EventName.buttonEnable) - if self.selfdrive.CS_prev.cruiseState.available: + if self.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasDisable) - if not (self.pause_lateral_on_brake_toggle and CS.brakePressed) and \ - not self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT): + if self.should_silent_lkas_enable(CS): if self.state_machine.state == State.paused: self.events_sp.add(EventNameSP.silentLkasEnable) @@ -141,7 +159,7 @@ class ModularAssistiveDrivingSystem: self.update_events(CS) - if not self.selfdrive.CP.passive and self.selfdrive.initialized: + if not self.CP.passive and self.selfdrive.initialized: self.enabled, self.active = self.state_machine.update() # Copy of previous SelfdriveD states for MADS events handling From 7565dd2545488f0b2100c03c753f562da158898a Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 17 May 2025 10:34:38 -0400 Subject: [PATCH 250/477] MADS: Refactor Unified Engagement Mode (#927) --- sunnypilot/mads/mads.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 1132a0df2f..dbaec258ab 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -69,13 +69,20 @@ class ModularAssistiveDrivingSystem: return True - def update_events(self, CS: structs.CarState): - def update_unified_engagement_mode(): - uem_blocked = self.enabled or (self.selfdrive.enabled and self.selfdrive.enabled_prev) - if (self.unified_engagement_mode and uem_blocked) or not self.unified_engagement_mode: - self.events.remove(EventName.pcmEnable) - self.events.remove(EventName.buttonEnable) + def block_unified_engagement_mode(self) -> bool: + # UEM disabled + if not self.unified_engagement_mode: + return True + if self.enabled: + return True + + if self.selfdrive.enabled and self.selfdrive.enabled_prev: + return True + + return False + + def update_events(self, CS: structs.CarState): def transition_paused_state(): if self.state_machine.state != State.paused: self.events_sp.add(EventNameSP.silentLkasDisable) @@ -115,7 +122,9 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.manualRestart) if self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable): - update_unified_engagement_mode() + if self.block_unified_engagement_mode(): + self.events.remove(EventName.pcmEnable) + self.events.remove(EventName.buttonEnable) else: if self.main_enabled_toggle: if CS.cruiseState.available and not self.CS_prev.cruiseState.available: From b92d717f2fb9114d0153214be487c7db565a056d Mon Sep 17 00:00:00 2001 From: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Date: Sat, 17 May 2025 09:33:18 -0700 Subject: [PATCH 251/477] Hyundai: add more FW versions to enable radar tracks debug script (#804) * fw version for my car, and added scc radar module firmware to enable radar points * bump points * bump --------- Co-authored-by: DevTekVE Co-authored-by: Jason Wen --- selfdrive/debug/hyundai_enable_radar_points.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/hyundai_enable_radar_points.py index 93f5949eac..30e2949378 100755 --- a/selfdrive/debug/hyundai_enable_radar_points.py +++ b/selfdrive/debug/hyundai_enable_radar_points.py @@ -71,6 +71,13 @@ SUPPORTED_FW_VERSIONS = { b"DLhe SCC FHCUP 1.00 1.02 99110-L7000 \x01 \x102 ": ConfigValues( default_config=b"\x00\x00\x00\x01\x00\x00", tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), + # 2022 Niro EV + b"DEev SCC F-CUP 1.00 1.00 99110-Q4600\x01\x42 ": ConfigValues( + default_config=b"\x00\x00\x00\x01\x00\x00", + tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), + b"DEev SCC F-CUP 1.00 1.00 99110-Q4600 \x07\x03\t% ": ConfigValues( + default_config=b"\x00\x00\x00\x01\x00\x00", + tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), } if __name__ == "__main__": From 5a67aa3618cd81eb06f8d1fe30f1df84914c75a5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 17 May 2025 12:00:09 -0700 Subject: [PATCH 252/477] test_translations: don't fail on rate limit (#35262) skip translation test on 429 --- selfdrive/ui/tests/test_translations.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index 09dd7c5d8b..2ae3356bb8 100644 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -96,8 +96,15 @@ class TestTranslations: match = re.search(r'_([a-zA-Z]{2,3})', self.file) assert match, f"{self.name} - could not parse language" - response = requests.get(f"https://raw.githubusercontent.com/LDNOOBW/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words/master/{match.group(1)}") - response.raise_for_status() + try: + response = requests.get( + f"https://raw.githubusercontent.com/LDNOOBW/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words/master/{match.group(1)}" + ) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + if e.response is not None and e.response.status_code == 429: + pytest.skip("word list rate limited") + raise banned_words = {line.strip() for line in response.text.splitlines()} From 1c68a073a023ce1a80204183fb2886d6e077db21 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Sat, 17 May 2025 12:00:24 -0700 Subject: [PATCH 253/477] feat(clip): enable ecam (#35259) --- tools/clip/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/clip/run.py b/tools/clip/run.py index f0c4369c6f..a84290f9c4 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -234,7 +234,7 @@ def clip( out, ] - replay_cmd = [REPLAY, '-c', '1', '-s', str(begin_at), '--prefix', prefix] + replay_cmd = [REPLAY, '--ecam', '-c', '1', '-s', str(begin_at), '--prefix', prefix] if data_dir: replay_cmd.extend(['--data_dir', data_dir]) if quality == 'low': From 77102a85322d799a12af98ea114c895ddaed7fe8 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 17 May 2025 17:31:27 -0700 Subject: [PATCH 254/477] Ford: support low gear (#35238) support low gear for ford --- selfdrive/car/car_specific.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index dc37519be1..355ab1a7c8 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -46,7 +46,7 @@ class CarSpecificEvents: events = Events() elif self.CP.brand == 'ford': - events = self.create_common_events(CS, CS_prev, extra_gears=[GearShifter.manumatic]) + events = self.create_common_events(CS, CS_prev, extra_gears=[GearShifter.low, GearShifter.manumatic]) elif self.CP.brand == 'nissan': events = self.create_common_events(CS, CS_prev, extra_gears=[GearShifter.brake]) From 065e0e6369a8c19b8b364fa6e5e871019dc93c93 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Sat, 17 May 2025 19:44:52 -0700 Subject: [PATCH 255/477] [bot] Update Python packages (#35264) Update Python packages Co-authored-by: Vehicle Researcher --- docs/CARS.md | 629 +++++++++++++++++++++++++------------------------- opendbc_repo | 2 +- panda | 2 +- tinygrad_repo | 2 +- uv.lock | 109 ++++----- 5 files changed, 373 insertions(+), 371 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 55871eba2f..cdaeb38890 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,321 +4,322 @@ A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 311 Supported Cars +# 312 Supported Cars -|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| -|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chrysler|Pacifica 2021-23|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Chrysler|Pacifica Hybrid 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| -|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Focus 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Focus Hybrid 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga Hybrid 2024|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Mustang Mach-E 2021-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G70 2018|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G70 2019-21|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G70 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|G90 2017-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV60 (Advanced Trim) 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV60 (Performance Trim) 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 Electrified (Australia Only) 2022[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV70 Electrified (with HDA II) 2023-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Genesis|GV80 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic Hatchback 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic Hatchback Hybrid 2023 (Europe only)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Civic Hatchback Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|HR-V 2019-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|HR-V 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Honda|Ridgeline 2017-25|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Azera 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Azera Hybrid 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Azera Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Custin 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (with HDA II) 2022-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (without HDA II) 2022-24[6](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 6 (with HDA II) 2023-24[6](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq Electric 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai O connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai O connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona Electric (with HDA II, Korea only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai I connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Nexo 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Santa Cruz 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Santa Fe 2021-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Staria 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson 2022[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson 2023-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson Hybrid 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Tucson Plug-in Hybrid 2024[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Carnival 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Carnival (China only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (Southeast Asia only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (with HDA II) 2022-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (without HDA II) 2022-24[6](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|K8 Hybrid (with HDA II) 2023[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro EV 2023-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Hybrid 2018|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Niro Plug-in Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento 2021-23[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento Hybrid 2021-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sorento Plug-in Hybrid 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sportage 2023-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Sportage Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Stinger 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|ES 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|ES 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|ES Hybrid 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|ES Hybrid 2019-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|IS 2022-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|LC 2024|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RC 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan[7](#footnotes)|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan[7](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan[7](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Nissan[7](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Forester 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Impreza 2017-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Impreza 2020-22|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Legacy 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|Outback 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|XV 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Subaru|XV 2020-21|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
|| -|Škoda|Fabia 2022-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| -|Škoda|Kamiq 2021-23[13,15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| -|Škoda|Karoq 2019-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Kodiaq 2017-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia 2015-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia RS 2016[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Octavia Scout 2017-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Škoda|Scala 2020-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| -|Škoda|Superb 2015-22[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla[11](#footnotes)|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla[11](#footnotes)|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla[11](#footnotes)|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Tesla[11](#footnotes)|Model Y (with HW4) 2024[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|C-HR 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|C-HR Hybrid 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2018-20|All|Stock|0 mph[12](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry 2021-24|All|openpilot|0 mph[12](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Corolla Hybrid (South America only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat 2015-22[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| -|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)|| -|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video|Setup Video| +|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Acura|RDX 2019-21|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chrysler|Pacifica 2021-23|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Chrysler|Pacifica Hybrid 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None||| +|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=MewJc9LYp9M| +|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=MewJc9LYp9M| +|Ford|Focus 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Focus Hybrid 2018[3](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Kuga Hybrid 2024|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ford|Mustang Mach-E 2021-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q4 connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://www.youtube.com/watch?v=uUGkH6C_EQU| +|Genesis|G70 2018|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|G70 2019-21|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|G70 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|G90 2017-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV60 (Advanced Trim) 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV60 (Performance Trim) 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV70 Electrified (Australia Only) 2022[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV70 Electrified (with HDA II) 2023-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Genesis|GV80 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai M connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[5](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback 2022-24|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback Hybrid 2023 (Europe only)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Civic Hatchback Hybrid 2025|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V 2015-16|Touring Trim|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|CR-V Hybrid 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Fit 2018-20|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Freed 2020|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|HR-V 2019-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|HR-V 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Honda Bosch A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Pilot 2016-22|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Honda|Ridgeline 2017-25|Honda Sensing|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Honda Nidec connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Azera 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Azera Hybrid 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Azera Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Custin 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq 5 (with HDA II) 2022-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq 5 (without HDA II) 2022-24[6](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq 6 (with HDA II) 2023-24[6](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq Electric 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai O connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai O connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona Electric (with HDA II, Korea only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai I connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Nexo 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Santa Cruz 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Santa Fe 2021-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Staria 2023[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Tucson 2022[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Tucson 2023-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Tucson Hybrid 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Tucson Plug-in Hybrid 2024[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Carnival 2022-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Carnival (China only) 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|EV6 (Southeast Asia only) 2022-24[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|EV6 (with HDA II) 2022-24[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|EV6 (without HDA II) 2022-24[6](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|K8 Hybrid (with HDA II) 2023[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro EV (with HDA II) 2025[6](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai R connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro EV (without HDA II) 2023-25[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Hybrid 2018|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai D connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Niro Plug-in Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai F connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai G connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sorento 2021-23[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sorento Hybrid 2021-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sorento Plug-in Hybrid 2022-23[6](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sportage 2023-24[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Sportage Hybrid 2023[6](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai N connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Stinger 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|ES 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|ES 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|ES Hybrid 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|ES Hybrid 2019-25|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|IS 2022-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|LC 2024|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RC 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ford Q3 connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Mazda connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Nissan[7](#footnotes)|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Nissan[7](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Nissan[7](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Nissan[7](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Nissan A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Ram connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://youtu.be/uaISd1j7Z4U| +|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||https://youtu.be/uaISd1j7Z4U| +|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Forester 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Impreza 2017-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Impreza 2020-22|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Legacy 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|Outback 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|XV 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Subaru|XV 2020-21|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
Tools- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep)
||| +|Škoda|Fabia 2022-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| +|Škoda|Kamiq 2021-23[13,15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| +|Škoda|Karoq 2019-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Škoda|Kodiaq 2017-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Škoda|Octavia 2015-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Škoda|Octavia RS 2016[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Škoda|Octavia Scout 2017-19[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Škoda|Scala 2020-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| +|Škoda|Superb 2015-22[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model 3 (with HW3) 2019-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model 3 (with HW4) 2024-25[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model Y (with HW3) 2020-23[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla A connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Tesla[11](#footnotes)|Model Y (with HW4) 2024[10](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Tesla B connector
- 1 USB-C coupler
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|C-HR 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|C-HR Hybrid 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Camry 2018-20|All|Stock|0 mph[12](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Camry 2021-24|All|openpilot|0 mph[12](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Corolla Hybrid (South America only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Jetta 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Passat 2015-22[14](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| +|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[17](#footnotes)||| +|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| +|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
||| ### Footnotes 1openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`.
diff --git a/opendbc_repo b/opendbc_repo index f960080e28..51203122f3 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit f960080e286401cdb432709589d582a6c065fd10 +Subproject commit 51203122f30162dcf778b700deb482b8b5ab3b72 diff --git a/panda b/panda index 6adac802b5..8bd83750c2 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 6adac802b5dce739e35b734b81b8d20fca4bf700 +Subproject commit 8bd83750c2c0a5b8f68a00717f7516ae612fb5ab diff --git a/tinygrad_repo b/tinygrad_repo index f726f79a9e..6f77b938d7 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit f726f79a9e1b3ab6dafb3dc012feb7901afdf721 +Subproject commit 6f77b938d77395a34f02c7d7d904d80fcd81b9cd diff --git a/uv.lock b/uv.lock index 5eafe7b636..cd9c84af69 100644 --- a/uv.lock +++ b/uv.lock @@ -1544,11 +1544,11 @@ wheels = [ [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] @@ -1612,16 +1612,16 @@ wheels = [ [[package]] name = "protobuf" -version = "6.30.2" +version = "6.31.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315, upload-time = "2025-03-26T19:12:57.394Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/48/718c1e104a2e89970a8ff3b06d87e152834b576c570a6908f8c17ba88d65/protobuf-6.31.0.tar.gz", hash = "sha256:314fab1a6a316469dc2dd46f993cbbe95c861ea6807da910becfe7475bc26ffe", size = 441644, upload-time = "2025-05-14T17:58:27.862Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148, upload-time = "2025-03-26T19:12:41.359Z" }, - { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003, upload-time = "2025-03-26T19:12:44.156Z" }, - { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579, upload-time = "2025-03-26T19:12:45.447Z" }, - { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319, upload-time = "2025-03-26T19:12:46.999Z" }, - { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212, upload-time = "2025-03-26T19:12:48.458Z" }, - { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062, upload-time = "2025-03-26T19:12:55.892Z" }, + { url = "https://files.pythonhosted.org/packages/b6/77/8671682038b08237c927215fa3296bc1c54e4086fe542c87017c1b626663/protobuf-6.31.0-cp310-abi3-win32.whl", hash = "sha256:10bd62802dfa0588649740a59354090eaf54b8322f772fbdcca19bc78d27f0d6", size = 423437, upload-time = "2025-05-14T17:58:16.116Z" }, + { url = "https://files.pythonhosted.org/packages/e4/07/cc9b0cbf7593f6ef8cf87fa9b0e55cd74c5cb526dd89ad84aa7d6547ef8d/protobuf-6.31.0-cp310-abi3-win_amd64.whl", hash = "sha256:3e987c99fd634be8347246a02123250f394ba20573c953de133dc8b2c107dd71", size = 435118, upload-time = "2025-05-14T17:58:18.591Z" }, + { url = "https://files.pythonhosted.org/packages/21/46/33f884aa8bc59114dc97e0d954ca4618c556483670236008c88fbb7e834f/protobuf-6.31.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2c812f0f96ceb6b514448cefeb1df54ec06dde456783f5099c0e2f8a0f2caa89", size = 425439, upload-time = "2025-05-14T17:58:19.709Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/9a676b50229ce37b12777d7b21de90ae7bc0f9505d07e72e2e8d47b8d165/protobuf-6.31.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:67ce50195e4e584275623b8e6bc6d3d3dfd93924bf6116b86b3b8975ab9e4571", size = 321950, upload-time = "2025-05-14T17:58:22.04Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a7/243fa2d3c1b7675d54744b32dacf30356f4c27c0d3ad940ca8745a1c6b2c/protobuf-6.31.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:5353e38844168a327acd2b2aa440044411cd8d1b6774d5701008bd1dba067c79", size = 320904, upload-time = "2025-05-14T17:58:23.438Z" }, + { url = "https://files.pythonhosted.org/packages/ee/01/1ed1d482960a5718fd99c82f6d79120181947cfd4667ec3944d448ed44a3/protobuf-6.31.0-py3-none-any.whl", hash = "sha256:6ac2e82556e822c17a8d23aa1190bbc1d06efb9c261981da95c71c9da09e9e23", size = 168558, upload-time = "2025-05-14T17:58:26.923Z" }, ] [[package]] @@ -1740,20 +1740,21 @@ wheels = [ [[package]] name = "pycryptodome" -version = "3.22.0" +version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/e6/099310419df5ada522ff34ffc2f1a48a11b37fc6a76f51a6854c182dbd3e/pycryptodome-3.22.0.tar.gz", hash = "sha256:fd7ab568b3ad7b77c908d7c3f7e167ec5a8f035c64ff74f10d47a4edd043d723", size = 4917300, upload-time = "2025-03-15T23:03:36.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/65/a05831c3e4bcd1bf6c2a034e399f74b3d6f30bb4e37e36b9c310c09dc8c0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:009e1c80eea42401a5bd5983c4bab8d516aef22e014a4705622e24e6d9d703c6", size = 2490637, upload-time = "2025-03-15T23:02:43.111Z" }, - { url = "https://files.pythonhosted.org/packages/5c/76/ff3c2e7a60d17c080c4c6120ebaf60f38717cd387e77f84da4dcf7f64ff0/pycryptodome-3.22.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3b76fa80daeff9519d7e9f6d9e40708f2fce36b9295a847f00624a08293f4f00", size = 1635372, upload-time = "2025-03-15T23:02:45.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/7f/cc5d6da0dbc36acd978d80a72b228e33aadaec9c4f91c93221166d8bdc05/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a31fa5914b255ab62aac9265654292ce0404f6b66540a065f538466474baedbc", size = 2177456, upload-time = "2025-03-15T23:02:47.688Z" }, - { url = "https://files.pythonhosted.org/packages/92/65/35f5063e68790602d892ad36e35ac723147232a9084d1999630045c34593/pycryptodome-3.22.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0092fd476701eeeb04df5cc509d8b739fa381583cda6a46ff0a60639b7cd70d", size = 2263744, upload-time = "2025-03-15T23:02:49.548Z" }, - { url = "https://files.pythonhosted.org/packages/cc/67/46acdd35b1081c3dbc72dc466b1b95b80d2f64cad3520f994a9b6c5c7d00/pycryptodome-3.22.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18d5b0ddc7cf69231736d778bd3ae2b3efb681ae33b64b0c92fb4626bb48bb89", size = 2303356, upload-time = "2025-03-15T23:02:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f9/a4f8a83384626098e3f55664519bec113002b9ef751887086ae63a53135a/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f6cf6aa36fcf463e622d2165a5ad9963b2762bebae2f632d719dfb8544903cf5", size = 2176714, upload-time = "2025-03-15T23:02:53.85Z" }, - { url = "https://files.pythonhosted.org/packages/88/65/e5f8c3a885f70a6e05c84844cd5542120576f4369158946e8cfc623a464d/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:aec7b40a7ea5af7c40f8837adf20a137d5e11a6eb202cde7e588a48fb2d871a8", size = 2337329, upload-time = "2025-03-15T23:02:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/b8/2a/25e0be2b509c28375c7f75c7e8d8d060773f2cce4856a1654276e3202339/pycryptodome-3.22.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d21c1eda2f42211f18a25db4eaf8056c94a8563cd39da3683f89fe0d881fb772", size = 2262255, upload-time = "2025-03-15T23:02:58.055Z" }, - { url = "https://files.pythonhosted.org/packages/41/58/60917bc4bbd91712e53ce04daf237a74a0ad731383a01288130672994328/pycryptodome-3.22.0-cp37-abi3-win32.whl", hash = "sha256:f02baa9f5e35934c6e8dcec91fcde96612bdefef6e442813b8ea34e82c84bbfb", size = 1763403, upload-time = "2025-03-15T23:03:00.616Z" }, - { url = "https://files.pythonhosted.org/packages/55/f4/244c621afcf7867e23f63cfd7a9630f14cfe946c9be7e566af6c3915bcde/pycryptodome-3.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:d086aed307e96d40c23c42418cbbca22ecc0ab4a8a0e24f87932eeab26c08627", size = 1794568, upload-time = "2025-03-15T23:03:03.189Z" }, + { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" }, + { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" }, + { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" }, + { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" }, ] [[package]] @@ -4744,27 +4745,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.11.9" +version = "0.11.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/e7/e55dda1c92cdcf34b677ebef17486669800de01e887b7831a1b8fdf5cb08/ruff-0.11.9.tar.gz", hash = "sha256:ebd58d4f67a00afb3a30bf7d383e52d0e036e6195143c6db7019604a05335517", size = 4132134, upload-time = "2025-05-09T16:19:41.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/4c/4a3c5a97faaae6b428b336dcca81d03ad04779f8072c267ad2bd860126bf/ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6", size = 4165632, upload-time = "2025-05-15T14:08:56.76Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/71/75dfb7194fe6502708e547941d41162574d1f579c4676a8eb645bf1a6842/ruff-0.11.9-py3-none-linux_armv6l.whl", hash = "sha256:a31a1d143a5e6f499d1fb480f8e1e780b4dfdd580f86e05e87b835d22c5c6f8c", size = 10335453, upload-time = "2025-05-09T16:18:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/74/fc/ad80c869b1732f53c4232bbf341f33c5075b2c0fb3e488983eb55964076a/ruff-0.11.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:66bc18ca783b97186a1f3100e91e492615767ae0a3be584e1266aa9051990722", size = 11072566, upload-time = "2025-05-09T16:19:01.432Z" }, - { url = "https://files.pythonhosted.org/packages/87/0d/0ccececef8a0671dae155cbf7a1f90ea2dd1dba61405da60228bbe731d35/ruff-0.11.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bd576cd06962825de8aece49f28707662ada6a1ff2db848d1348e12c580acbf1", size = 10435020, upload-time = "2025-05-09T16:19:03.897Z" }, - { url = "https://files.pythonhosted.org/packages/52/01/e249e1da6ad722278094e183cbf22379a9bbe5f21a3e46cef24ccab76e22/ruff-0.11.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b1d18b4be8182cc6fddf859ce432cc9631556e9f371ada52f3eaefc10d878de", size = 10593935, upload-time = "2025-05-09T16:19:06.455Z" }, - { url = "https://files.pythonhosted.org/packages/ed/9a/40cf91f61e3003fe7bd43f1761882740e954506c5a0f9097b1cff861f04c/ruff-0.11.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f3f46f759ac623e94824b1e5a687a0df5cd7f5b00718ff9c24f0a894a683be7", size = 10172971, upload-time = "2025-05-09T16:19:10.261Z" }, - { url = "https://files.pythonhosted.org/packages/61/12/d395203de1e8717d7a2071b5a340422726d4736f44daf2290aad1085075f/ruff-0.11.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f34847eea11932d97b521450cf3e1d17863cfa5a94f21a056b93fb86f3f3dba2", size = 11748631, upload-time = "2025-05-09T16:19:12.307Z" }, - { url = "https://files.pythonhosted.org/packages/66/d6/ef4d5eba77677eab511644c37c55a3bb8dcac1cdeb331123fe342c9a16c9/ruff-0.11.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f33b15e00435773df97cddcd263578aa83af996b913721d86f47f4e0ee0ff271", size = 12409236, upload-time = "2025-05-09T16:19:15.006Z" }, - { url = "https://files.pythonhosted.org/packages/c5/8f/5a2c5fc6124dd925a5faf90e1089ee9036462118b619068e5b65f8ea03df/ruff-0.11.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b27613a683b086f2aca8996f63cb3dd7bc49e6eccf590563221f7b43ded3f65", size = 11881436, upload-time = "2025-05-09T16:19:17.063Z" }, - { url = "https://files.pythonhosted.org/packages/39/d1/9683f469ae0b99b95ef99a56cfe8c8373c14eba26bd5c622150959ce9f64/ruff-0.11.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e0d88756e63e8302e630cee3ce2ffb77859797cc84a830a24473939e6da3ca6", size = 13982759, upload-time = "2025-05-09T16:19:19.693Z" }, - { url = "https://files.pythonhosted.org/packages/4e/0b/c53a664f06e0faab596397867c6320c3816df479e888fe3af63bc3f89699/ruff-0.11.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:537c82c9829d7811e3aa680205f94c81a2958a122ac391c0eb60336ace741a70", size = 11541985, upload-time = "2025-05-09T16:19:21.831Z" }, - { url = "https://files.pythonhosted.org/packages/23/a0/156c4d7e685f6526a636a60986ee4a3c09c8c4e2a49b9a08c9913f46c139/ruff-0.11.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:440ac6a7029f3dee7d46ab7de6f54b19e34c2b090bb4f2480d0a2d635228f381", size = 10465775, upload-time = "2025-05-09T16:19:24.401Z" }, - { url = "https://files.pythonhosted.org/packages/43/d5/88b9a6534d9d4952c355e38eabc343df812f168a2c811dbce7d681aeb404/ruff-0.11.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:71c539bac63d0788a30227ed4d43b81353c89437d355fdc52e0cda4ce5651787", size = 10170957, upload-time = "2025-05-09T16:19:27.08Z" }, - { url = "https://files.pythonhosted.org/packages/f0/b8/2bd533bdaf469dc84b45815ab806784d561fab104d993a54e1852596d581/ruff-0.11.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c67117bc82457e4501473c5f5217d49d9222a360794bfb63968e09e70f340abd", size = 11143307, upload-time = "2025-05-09T16:19:29.462Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d9/43cfba291788459b9bfd4e09a0479aa94d05ab5021d381a502d61a807ec1/ruff-0.11.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e4b78454f97aa454586e8a5557facb40d683e74246c97372af3c2d76901d697b", size = 11603026, upload-time = "2025-05-09T16:19:31.569Z" }, - { url = "https://files.pythonhosted.org/packages/22/e6/7ed70048e89b01d728ccc950557a17ecf8df4127b08a56944b9d0bae61bc/ruff-0.11.9-py3-none-win32.whl", hash = "sha256:7fe1bc950e7d7b42caaee2a8a3bc27410547cc032c9558ee2e0f6d3b209e845a", size = 10548627, upload-time = "2025-05-09T16:19:33.657Z" }, - { url = "https://files.pythonhosted.org/packages/90/36/1da5d566271682ed10f436f732e5f75f926c17255c9c75cefb77d4bf8f10/ruff-0.11.9-py3-none-win_amd64.whl", hash = "sha256:52edaa4a6d70f8180343a5b7f030c7edd36ad180c9f4d224959c2d689962d964", size = 11634340, upload-time = "2025-05-09T16:19:35.815Z" }, - { url = "https://files.pythonhosted.org/packages/40/f7/70aad26e5877c8f7ee5b161c4c9fa0100e63fc4c944dc6d97b9c7e871417/ruff-0.11.9-py3-none-win_arm64.whl", hash = "sha256:bcf42689c22f2e240f496d0c183ef2c6f7b35e809f12c1db58f75d9aa8d630ca", size = 10741080, upload-time = "2025-05-09T16:19:39.605Z" }, + { url = "https://files.pythonhosted.org/packages/2f/9f/596c628f8824a2ce4cd12b0f0b4c0629a62dfffc5d0f742c19a1d71be108/ruff-0.11.10-py3-none-linux_armv6l.whl", hash = "sha256:859a7bfa7bc8888abbea31ef8a2b411714e6a80f0d173c2a82f9041ed6b50f58", size = 10316243, upload-time = "2025-05-15T14:08:12.884Z" }, + { url = "https://files.pythonhosted.org/packages/3c/38/c1e0b77ab58b426f8c332c1d1d3432d9fc9a9ea622806e208220cb133c9e/ruff-0.11.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:968220a57e09ea5e4fd48ed1c646419961a0570727c7e069842edd018ee8afed", size = 11083636, upload-time = "2025-05-15T14:08:16.551Z" }, + { url = "https://files.pythonhosted.org/packages/23/41/b75e15961d6047d7fe1b13886e56e8413be8467a4e1be0a07f3b303cd65a/ruff-0.11.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1067245bad978e7aa7b22f67113ecc6eb241dca0d9b696144256c3a879663bca", size = 10441624, upload-time = "2025-05-15T14:08:19.032Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2c/e396b6703f131406db1811ea3d746f29d91b41bbd43ad572fea30da1435d/ruff-0.11.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4854fd09c7aed5b1590e996a81aeff0c9ff51378b084eb5a0b9cd9518e6cff2", size = 10624358, upload-time = "2025-05-15T14:08:21.542Z" }, + { url = "https://files.pythonhosted.org/packages/bd/8c/ee6cca8bdaf0f9a3704796022851a33cd37d1340bceaf4f6e991eb164e2e/ruff-0.11.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b4564e9f99168c0f9195a0fd5fa5928004b33b377137f978055e40008a082c5", size = 10176850, upload-time = "2025-05-15T14:08:23.682Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ce/4e27e131a434321b3b7c66512c3ee7505b446eb1c8a80777c023f7e876e6/ruff-0.11.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b6a9cc5b62c03cc1fea0044ed8576379dbaf751d5503d718c973d5418483641", size = 11759787, upload-time = "2025-05-15T14:08:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/58/de/1e2e77fc72adc7cf5b5123fd04a59ed329651d3eab9825674a9e640b100b/ruff-0.11.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:607ecbb6f03e44c9e0a93aedacb17b4eb4f3563d00e8b474298a201622677947", size = 12430479, upload-time = "2025-05-15T14:08:28.013Z" }, + { url = "https://files.pythonhosted.org/packages/07/ed/af0f2340f33b70d50121628ef175523cc4c37619e98d98748c85764c8d88/ruff-0.11.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3a522fa389402cd2137df9ddefe848f727250535c70dafa840badffb56b7a4", size = 11919760, upload-time = "2025-05-15T14:08:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/24/09/d7b3d3226d535cb89234390f418d10e00a157b6c4a06dfbe723e9322cb7d/ruff-0.11.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f071b0deed7e9245d5820dac235cbdd4ef99d7b12ff04c330a241ad3534319f", size = 14041747, upload-time = "2025-05-15T14:08:33.297Z" }, + { url = "https://files.pythonhosted.org/packages/62/b3/a63b4e91850e3f47f78795e6630ee9266cb6963de8f0191600289c2bb8f4/ruff-0.11.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a60e3a0a617eafba1f2e4186d827759d65348fa53708ca547e384db28406a0b", size = 11550657, upload-time = "2025-05-15T14:08:35.639Z" }, + { url = "https://files.pythonhosted.org/packages/46/63/a4f95c241d79402ccdbdb1d823d156c89fbb36ebfc4289dce092e6c0aa8f/ruff-0.11.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:da8ec977eaa4b7bf75470fb575bea2cb41a0e07c7ea9d5a0a97d13dbca697bf2", size = 10489671, upload-time = "2025-05-15T14:08:38.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9b/c2238bfebf1e473495659c523d50b1685258b6345d5ab0b418ca3f010cd7/ruff-0.11.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ddf8967e08227d1bd95cc0851ef80d2ad9c7c0c5aab1eba31db49cf0a7b99523", size = 10160135, upload-time = "2025-05-15T14:08:41.247Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ef/ba7251dd15206688dbfba7d413c0312e94df3b31b08f5d695580b755a899/ruff-0.11.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5a94acf798a82db188f6f36575d80609072b032105d114b0f98661e1679c9125", size = 11170179, upload-time = "2025-05-15T14:08:43.762Z" }, + { url = "https://files.pythonhosted.org/packages/73/9f/5c336717293203ba275dbfa2ea16e49b29a9fd9a0ea8b6febfc17e133577/ruff-0.11.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3afead355f1d16d95630df28d4ba17fb2cb9c8dfac8d21ced14984121f639bad", size = 11626021, upload-time = "2025-05-15T14:08:46.451Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2b/162fa86d2639076667c9aa59196c020dc6d7023ac8f342416c2f5ec4bda0/ruff-0.11.10-py3-none-win32.whl", hash = "sha256:dc061a98d32a97211af7e7f3fa1d4ca2fcf919fb96c28f39551f35fc55bdbc19", size = 10494958, upload-time = "2025-05-15T14:08:49.601Z" }, + { url = "https://files.pythonhosted.org/packages/24/f3/66643d8f32f50a4b0d09a4832b7d919145ee2b944d43e604fbd7c144d175/ruff-0.11.10-py3-none-win_amd64.whl", hash = "sha256:5cc725fbb4d25b0f185cb42df07ab6b76c4489b4bfb740a175f3a59c70e8a224", size = 11650285, upload-time = "2025-05-15T14:08:52.392Z" }, + { url = "https://files.pythonhosted.org/packages/95/3a/2e8704d19f376c799748ff9cb041225c1d59f3e7711bc5596c8cfdc24925/ruff-0.11.10-py3-none-win_arm64.whl", hash = "sha256:ef69637b35fb8b210743926778d0e45e1bffa850a7c61e428c6b971549b5f5d1", size = 10765278, upload-time = "2025-05-15T14:08:54.56Z" }, ] [[package]] @@ -4823,11 +4824,11 @@ wheels = [ [[package]] name = "setuptools" -version = "80.4.0" +version = "80.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/0cc40fe41fd2adb80a2f388987f4f8db3c866c69e33e0b4c8b093fdf700e/setuptools-80.4.0.tar.gz", hash = "sha256:5a78f61820bc088c8e4add52932ae6b8cf423da2aff268c23f813cfbb13b4006", size = 1315008, upload-time = "2025-05-09T20:42:27.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/8b/dc1773e8e5d07fd27c1632c45c1de856ac3dbf09c0147f782ca6d990cf15/setuptools-80.7.1.tar.gz", hash = "sha256:f6ffc5f0142b1bd8d0ca94ee91b30c0ca862ffd50826da1ea85258a06fd94552", size = 1319188, upload-time = "2025-05-15T02:41:00.955Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/93/dba5ed08c2e31ec7cdc2ce75705a484ef0be1a2fecac8a58272489349de8/setuptools-80.4.0-py3-none-any.whl", hash = "sha256:6cdc8cb9a7d590b237dbe4493614a9b75d0559b888047c1f67d49ba50fc3edb2", size = 1200812, upload-time = "2025-05-09T20:42:25.325Z" }, + { url = "https://files.pythonhosted.org/packages/a1/18/0e835c3a557dc5faffc8f91092f62fc337c1dab1066715842e7a4b318ec4/setuptools-80.7.1-py3-none-any.whl", hash = "sha256:ca5cc1069b85dc23070a6628e6bcecb3292acac802399c7f8edc0100619f9009", size = 1200776, upload-time = "2025-05-15T02:40:58.887Z" }, ] [[package]] @@ -4906,17 +4907,17 @@ wheels = [ [[package]] name = "sounddevice" -version = "0.5.1" +version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/80/2d/b04ae180312b81dbb694504bee170eada5372242e186f6298139fd3a0513/sounddevice-0.5.1.tar.gz", hash = "sha256:09ca991daeda8ce4be9ac91e15a9a81c8f81efa6b695a348c9171ea0c16cb041", size = 52896, upload-time = "2024-10-12T09:40:12.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/a6/91e9f08ed37c7c9f56b5227c6aea7f2ae63ba2d59520eefb24e82cbdd589/sounddevice-0.5.2.tar.gz", hash = "sha256:c634d51bd4e922d6f0fa5e1a975cc897c947f61d31da9f79ba7ea34dff448b49", size = 53150, upload-time = "2025-05-16T18:12:27.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/d1/464b5fca3decdd0cfec8c47f7b4161a0b12972453201c1bf03811f367c5e/sounddevice-0.5.1-py3-none-any.whl", hash = "sha256:e2017f182888c3f3c280d9fbac92e5dbddac024a7e3442f6e6116bd79dab8a9c", size = 32276, upload-time = "2024-10-12T09:40:05.605Z" }, - { url = "https://files.pythonhosted.org/packages/6f/f6/6703fe7cf3d7b7279040c792aeec6334e7305956aba4a80f23e62c8fdc44/sounddevice-0.5.1-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:d16cb23d92322526a86a9490c427bf8d49e273d9ccc0bd096feecd229cde6031", size = 107916, upload-time = "2024-10-12T09:40:07.436Z" }, - { url = "https://files.pythonhosted.org/packages/57/a5/78a5e71f5ec0faedc54f4053775d61407bfbd7d0c18228c7f3d4252fd276/sounddevice-0.5.1-py3-none-win32.whl", hash = "sha256:d84cc6231526e7a08e89beff229c37f762baefe5e0cc2747cbe8e3a565470055", size = 312494, upload-time = "2024-10-12T09:40:09.355Z" }, - { url = "https://files.pythonhosted.org/packages/af/9b/15217b04f3b36d30de55fef542389d722de63f1ad81f9c72d8afc98cb6ab/sounddevice-0.5.1-py3-none-win_amd64.whl", hash = "sha256:4313b63f2076552b23ac3e0abd3bcfc0c1c6a696fc356759a13bd113c9df90f1", size = 363634, upload-time = "2024-10-12T09:40:11.065Z" }, + { url = "https://files.pythonhosted.org/packages/75/2d/582738fc01352a5bc20acac9221e58538365cecb3bb264838f66419df219/sounddevice-0.5.2-py3-none-any.whl", hash = "sha256:82375859fac2e73295a4ab3fc60bd4782743157adc339561c1f1142af472f505", size = 32450, upload-time = "2025-05-16T18:12:21.919Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6f/e3dd751face4fcb5be25e8abba22f25d8e6457ebd7e9ed79068b768dc0e5/sounddevice-0.5.2-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:943f27e66037d41435bdd0293454072cdf657b594c9cde63cd01ee3daaac7ab3", size = 108088, upload-time = "2025-05-16T18:12:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/45/0b/bfad79af0b380aa7c0bfe73e4b03e0af45354a48ad62549489bd7696c5b0/sounddevice-0.5.2-py3-none-win32.whl", hash = "sha256:3a113ce614a2c557f14737cb20123ae6298c91fc9301eb014ada0cba6d248c5f", size = 312665, upload-time = "2025-05-16T18:12:24.726Z" }, + { url = "https://files.pythonhosted.org/packages/e1/3e/61d88e6b0a7383127cdc779195cb9d83ebcf11d39bc961de5777e457075e/sounddevice-0.5.2-py3-none-win_amd64.whl", hash = "sha256:e18944b767d2dac3771a7771bdd7ff7d3acd7d334e72c4bedab17d1aed5dbc22", size = 363808, upload-time = "2025-05-16T18:12:26Z" }, ] [[package]] @@ -4989,14 +4990,14 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.0.20250328" +version = "2.32.0.20250515" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995, upload-time = "2025-03-28T02:55:13.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/c1/cdc4f9b8cfd9130fbe6276db574f114541f4231fcc6fb29648289e6e3390/types_requests-2.32.0.20250515.tar.gz", hash = "sha256:09c8b63c11318cb2460813871aaa48b671002e59fda67ca909e9883777787581", size = 23012, upload-time = "2025-05-15T03:04:31.817Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663, upload-time = "2025-03-28T02:55:11.946Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/68a997c73a129287785f418c1ebb6004f81e46b53b3caba88c0e03fcd04a/types_requests-2.32.0.20250515-py3-none-any.whl", hash = "sha256:f8eba93b3a892beee32643ff836993f15a785816acca21ea0ffa006f05ef0fb2", size = 20635, upload-time = "2025-05-15T03:04:30.5Z" }, ] [[package]] From 80679b74e6256637b35dfe15cfec925e68c83242 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 17 May 2025 23:07:05 -0400 Subject: [PATCH 256/477] MADS: prep for refactor (#930) * MADS: prep for refactor * no longer --- sunnypilot/mads/mads.py | 51 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index dbaec258ab..e6665484ae 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -26,7 +26,6 @@ IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput) class ModularAssistiveDrivingSystem: def __init__(self, selfdrive): self.CP = selfdrive.CP - self.CS_prev = selfdrive.CS_prev self.params = selfdrive.params self.enabled = False @@ -55,7 +54,7 @@ class ModularAssistiveDrivingSystem: self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode") def pedal_pressed_non_gas_pressed(self, CS: structs.CarState) -> bool: - if self.events.has(EventName.pedalPressed) and not (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator): + if self.events.has(EventName.pedalPressed) and not (CS.gasPressed and not self.selfdrive.CS_prev.gasPressed and self.disengage_on_accelerator): return True return False @@ -82,38 +81,38 @@ class ModularAssistiveDrivingSystem: return False + def transition_paused_state(self): + if self.state_machine.state != State.paused: + self.events_sp.add(EventNameSP.silentLkasDisable) + + def replace_event(self, old_event: int, new_event: int): + self.events.remove(old_event) + self.events_sp.add(new_event) + def update_events(self, CS: structs.CarState): - def transition_paused_state(): - if self.state_machine.state != State.paused: - self.events_sp.add(EventNameSP.silentLkasDisable) - - def replace_event(old_event: int, new_event: int): - self.events.remove(old_event) - self.events_sp.add(new_event) - if not self.selfdrive.enabled and self.enabled: if self.events.has(EventName.doorOpen): - replace_event(EventName.doorOpen, EventNameSP.silentDoorOpen) - transition_paused_state() + self.replace_event(EventName.doorOpen, EventNameSP.silentDoorOpen) + self.transition_paused_state() if self.events.has(EventName.seatbeltNotLatched): - replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched) - transition_paused_state() + self.replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched) + self.transition_paused_state() if self.events.has(EventName.wrongGear) and (CS.vEgo < 2.5 or CS.gearShifter == GearShifter.reverse): - replace_event(EventName.wrongGear, EventNameSP.silentWrongGear) - transition_paused_state() + self.replace_event(EventName.wrongGear, EventNameSP.silentWrongGear) + self.transition_paused_state() if self.events.has(EventName.reverseGear): - replace_event(EventName.reverseGear, EventNameSP.silentReverseGear) - transition_paused_state() + self.replace_event(EventName.reverseGear, EventNameSP.silentReverseGear) + self.transition_paused_state() if self.events.has(EventName.brakeHold): - replace_event(EventName.brakeHold, EventNameSP.silentBrakeHold) - transition_paused_state() + self.replace_event(EventName.brakeHold, EventNameSP.silentBrakeHold) + self.transition_paused_state() if self.events.has(EventName.parkBrake): - replace_event(EventName.parkBrake, EventNameSP.silentParkBrake) - transition_paused_state() + self.replace_event(EventName.parkBrake, EventNameSP.silentParkBrake) + self.transition_paused_state() if self.pause_lateral_on_brake_toggle: if self.pedal_pressed_non_gas_pressed(CS): - transition_paused_state() + self.transition_paused_state() self.events.remove(EventName.preEnableStandstill) self.events.remove(EventName.belowEngageSpeed) @@ -127,7 +126,7 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.buttonEnable) else: if self.main_enabled_toggle: - if CS.cruiseState.available and not self.CS_prev.cruiseState.available: + if CS.cruiseState.available and not self.selfdrive.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasEnable) for be in CS.buttonEvents: @@ -145,7 +144,7 @@ class ModularAssistiveDrivingSystem: if not CS.cruiseState.available: self.events.remove(EventName.buttonEnable) - if self.CS_prev.cruiseState.available: + if self.selfdrive.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasDisable) if self.should_silent_lkas_enable(CS): @@ -158,7 +157,7 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.wrongCruiseMode) if any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents): if self.events.has(EventName.wrongCarMode): - replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) + self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) else: self.events.remove(EventName.wrongCarMode) From 4dbabf4e24a6dddf477858ed149d1a447c55b2de Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 17 May 2025 23:34:55 -0400 Subject: [PATCH 257/477] MADS: retain `pedalPressed` alert only (#928) * MADS: Refactor Unified Engagement Mode * init * bring back pedal pressed event while trying to engage long * MADS: prep for refactor * no longer * this is cleaner? * in another pr --- cereal/custom.capnp | 1 + sunnypilot/mads/mads.py | 7 ++++++- sunnypilot/selfdrive/selfdrived/events.py | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 2a72a59a10..eb932b484e 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -144,6 +144,7 @@ struct OnroadEventSP @0xda96579883444c35 { hyundaiRadarTracksConfirmed @13; experimentalModeSwitched @14; wrongCarModeAlertOnly @15; + pedalPressedAlertOnly @16; } } diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index e6665484ae..456be01882 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -120,7 +120,12 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.cruiseDisabled) self.events.remove(EventName.manualRestart) - if self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable): + selfdrive_enable_events = self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable) + + if selfdrive_enable_events: + if self.pedal_pressed_non_gas_pressed(CS): + self.events_sp.add(EventNameSP.pedalPressedAlertOnly) + if self.block_unified_engagement_mode(): self.events.remove(EventName.pcmEnable) self.events.remove(EventName.buttonEnable) diff --git a/sunnypilot/selfdrive/selfdrived/events.py b/sunnypilot/selfdrive/selfdrived/events.py index 979de0a80c..67672649c5 100644 --- a/sunnypilot/selfdrive/selfdrived/events.py +++ b/sunnypilot/selfdrive/selfdrived/events.py @@ -134,4 +134,8 @@ EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = { ET.WARNING: wrong_car_mode_alert, }, + EventNameSP.pedalPressedAlertOnly: { + ET.WARNING: NoEntryAlert("Pedal Pressed") + } + } From 6d516a7704410b69a2707dfcb856eba5be519f81 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 18 May 2025 00:18:41 -0400 Subject: [PATCH 258/477] MADS: `wrongCarMode` alert only with selfdrive enable (#931) * MADS: Refactor Unified Engagement Mode * init * bring back pedal pressed event while trying to engage long * MADS: prep for refactor * no longer * this is cleaner? * in another pr * Revert "in another pr" This reverts commit 31aec8a7aabc798c1b19c365488efe04a47869e9. * less * rename * type hint --- sunnypilot/mads/mads.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 456be01882..d5ce9354c1 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -81,6 +81,13 @@ class ModularAssistiveDrivingSystem: return False + def get_wrong_car_mode(self, alert_only: bool) -> None: + if alert_only: + if self.events.has(EventName.wrongCarMode): + self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) + else: + self.events.remove(EventName.wrongCarMode) + def transition_paused_state(self): if self.state_machine.state != State.paused: self.events_sp.add(EventNameSP.silentLkasDisable) @@ -121,11 +128,12 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.manualRestart) selfdrive_enable_events = self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable) + set_speed_btns_enable = any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents) + + # wrongCarMode alert only or actively block control + self.get_wrong_car_mode(selfdrive_enable_events or set_speed_btns_enable) if selfdrive_enable_events: - if self.pedal_pressed_non_gas_pressed(CS): - self.events_sp.add(EventNameSP.pedalPressedAlertOnly) - if self.block_unified_engagement_mode(): self.events.remove(EventName.pcmEnable) self.events.remove(EventName.buttonEnable) @@ -160,11 +168,6 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.buttonCancel) self.events.remove(EventName.pedalPressed) self.events.remove(EventName.wrongCruiseMode) - if any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents): - if self.events.has(EventName.wrongCarMode): - self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) - else: - self.events.remove(EventName.wrongCarMode) def update(self, CS: structs.CarState): if not self.enabled_toggle: From 8427bf76cfc3c217188fa833ab0065aa5dc18f0e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 17 May 2025 22:54:01 -0700 Subject: [PATCH 259/477] typo --- selfdrive/controls/lib/latcontrol_angle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/lib/latcontrol_angle.py b/selfdrive/controls/lib/latcontrol_angle.py index f94dca57c1..f3f1bc5acf 100644 --- a/selfdrive/controls/lib/latcontrol_angle.py +++ b/selfdrive/controls/lib/latcontrol_angle.py @@ -24,7 +24,7 @@ class LatControlAngle(LatControl): angle_steers_des += params.angleOffsetDeg if self.use_steer_limited_by_controls: - # these cars' carcontrolers calculate max lateral accel and jerk, so we can rely on carOutput for saturation + # these cars' carcontrollers calculate max lateral accel and jerk, so we can rely on carOutput for saturation angle_control_saturated = steer_limited_by_controls else: # for cars which use a method of limiting torque such as a torque signal (Nissan and Toyota) From 66c0971a0f95f0af21ad2948198cad53c6ab1700 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 17 May 2025 23:56:21 -0700 Subject: [PATCH 260/477] migration: remove disengage on gas alternative experience (#35267) * fix * done * test * update refs --- selfdrive/test/process_replay/migration.py | 14 ++++++++------ selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/selfdrive/test/process_replay/migration.py b/selfdrive/test/process_replay/migration.py index 02cd9b6d71..33b363cfd9 100644 --- a/selfdrive/test/process_replay/migration.py +++ b/selfdrive/test/process_replay/migration.py @@ -22,12 +22,12 @@ MigrationOps = tuple[list[tuple[int, capnp.lib.capnp._DynamicStructReader]], lis MigrationFunc = Callable[[list[MessageWithIndex]], MigrationOps] -## rules for migration functions -## 1. must use the decorator @migration(inputs=[...], product="...") and MigrationFunc signature -## 2. it only gets the messages that are in the inputs list -## 3. product is the message type created by the migration function, and the function will be skipped if product type already exists in lr -## 4. it must return a list of operations to be applied to the logreader (replace, add, delete) -## 5. all migration functions must be independent of each other +# rules for migration functions +# 1. must use the decorator @migration(inputs=[...], product="...") and MigrationFunc signature +# 2. it only gets the messages that are in the inputs list +# 3. product is the message type created by the migration function, and the function will be skipped if product type already exists in lr +# 4. it must return a list of operations to be applied to the logreader (replace, add, delete) +# 5. all migration functions must be independent of each other def migrate_all(lr: LogIterable, manager_states: bool = False, panda_states: bool = False, camera_states: bool = False): migrations = [ migrate_sensorEvents, @@ -306,6 +306,8 @@ def migrate_pandaStates(msgs): elif msg.which() == 'pandaStates': new_msg = msg.as_builder() new_msg.pandaStates[-1].safetyParam = safety_param + # Clear DISABLE_DISENGAGE_ON_GAS bit to fix controls mismatch + new_msg.pandaStates[-1].alternativeExperience &= ~1 ops.append((index, new_msg.as_reader())) return ops, [], [] diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index c97f9a6bde..c8e879e58a 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -bc0a2e6eac422e574b6c670fe493d368df10ef42 \ No newline at end of file +6602b004aa91decf3a1a4ab7ddaf77a99d7ebfc3 \ No newline at end of file From 33721cf5fab014f945df783fc3267f4081c23401 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 18 May 2025 00:38:57 -0700 Subject: [PATCH 261/477] Lower Tesla steer limit timer (#35268) * bump * update refs --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 51203122f3..488e31ccba 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 51203122f30162dcf778b700deb482b8b5ab3b72 +Subproject commit 488e31ccbab507315648061c23308cedc5284cf2 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index c8e879e58a..8e8a88db2c 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -6602b004aa91decf3a1a4ab7ddaf77a99d7ebfc3 \ No newline at end of file +2d23b1ba470927cefa646b95e1c8b7ac0f944f60 \ No newline at end of file From a6a09276e94da4b526de4f7604b1a6b448842426 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 18 May 2025 00:52:02 -0700 Subject: [PATCH 262/477] Hyundai: fix recent enable button press regression (#35265) * move back to port * much better * use the CS field now * bump * fix * need to switch so it works on ports that don't use it, and makes more sense * bump * bump * update refs * make sense * master --- opendbc_repo | 2 +- selfdrive/car/car_specific.py | 15 +++------------ selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 488e31ccba..0af0d464df 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 488e31ccbab507315648061c23308cedc5284cf2 +Subproject commit 0af0d464df30daa4c78f5349c958ba584370ef80 diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index 355ab1a7c8..67d1010d75 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -1,11 +1,8 @@ -from collections import deque from cereal import car, log import cereal.messaging as messaging from opendbc.car import DT_CTRL, structs from opendbc.car.interfaces import MAX_CTRL_SPEED from opendbc.car.volkswagen.values import CarControllerParams as VWCarControllerParams -from opendbc.car.hyundai.interface import ENABLE_BUTTONS as HYUNDAI_ENABLE_BUTTONS -from opendbc.car.hyundai.carstate import PREV_BUTTON_SAMPLES as HYUNDAI_PREV_BUTTON_SAMPLES from openpilot.selfdrive.selfdrived.events import Events @@ -39,8 +36,6 @@ class CarSpecificEvents: self.no_steer_warning = False self.silent_steer_warning = True - self.cruise_buttons: deque = deque([], maxlen=HYUNDAI_PREV_BUTTON_SAMPLES) - def update(self, CS: car.CarState, CS_prev: car.CarState, CC: car.CarControl): if self.CP.brand in ('body', 'mock'): events = Events() @@ -136,12 +131,8 @@ class CarSpecificEvents: # events.add(EventName.steerTimeLimit) elif self.CP.brand == 'hyundai': - # On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state - # To avoid re-engaging when openpilot cancels, check user engagement intention via buttons - # Main button also can trigger an engagement on these cars - self.cruise_buttons.append(any(ev.type in HYUNDAI_ENABLE_BUTTONS for ev in CS.buttonEvents)) events = self.create_common_events(CS, CS_prev, extra_gears=(GearShifter.sport, GearShifter.manumatic), - pcm_enable=self.CP.pcmCruise, allow_enable=any(self.cruise_buttons), allow_button_cancel=False) + pcm_enable=self.CP.pcmCruise, allow_button_cancel=False) # low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s) if CS.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.: @@ -157,7 +148,7 @@ class CarSpecificEvents: return events def create_common_events(self, CS: structs.CarState, CS_prev: car.CarState, extra_gears=None, pcm_enable=True, - allow_enable=True, allow_button_cancel=True): + allow_button_cancel=True): events = Events() if CS.doorOpen: @@ -236,7 +227,7 @@ class CarSpecificEvents: # we engage when pcm is active (rising edge) # enabling can optionally be blocked by the car interface if pcm_enable: - if CS.cruiseState.enabled and not CS_prev.cruiseState.enabled and allow_enable: + if CS.cruiseState.enabled and not CS_prev.cruiseState.enabled and not CS.blockPcmEnable: events.add(EventName.pcmEnable) elif not CS.cruiseState.enabled: events.add(EventName.pcmDisable) diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 8e8a88db2c..f67c4540c6 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -2d23b1ba470927cefa646b95e1c8b7ac0f944f60 \ No newline at end of file +746da1900c43a64146bb485b7fc2a836e2005113 \ No newline at end of file From 8e57f774d0e8302e1bf1834f84720bb1f9211ae3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 18 May 2025 01:05:15 -0700 Subject: [PATCH 263/477] Hyundai: move low speed steering alert to car state (#35269) * hkg can: move low speed alert to carstate * bump --- opendbc_repo | 2 +- selfdrive/car/car_specific.py | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 0af0d464df..bf27fea810 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0af0d464df30daa4c78f5349c958ba584370ef80 +Subproject commit bf27fea810a111bd9b2c22a20e713485c65a2043 diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index 67d1010d75..c5edb484af 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -134,14 +134,6 @@ class CarSpecificEvents: events = self.create_common_events(CS, CS_prev, extra_gears=(GearShifter.sport, GearShifter.manumatic), pcm_enable=self.CP.pcmCruise, allow_button_cancel=False) - # low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s) - if CS.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.: - self.low_speed_alert = True - if CS.vEgo > (self.CP.minSteerSpeed + 4.): - self.low_speed_alert = False - if self.low_speed_alert: - events.add(EventName.belowSteerSpeed) - else: events = self.create_common_events(CS, CS_prev) From e8fd8e601864b94e921878bd7359c8bd18562572 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 18 May 2025 01:20:28 -0700 Subject: [PATCH 264/477] bump opendbc --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index bf27fea810..447381dee2 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit bf27fea810a111bd9b2c22a20e713485c65a2043 +Subproject commit 447381dee2d30fb46e4bf1aec0529667d0b9d159 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index f67c4540c6..f916e7b306 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -746da1900c43a64146bb485b7fc2a836e2005113 \ No newline at end of file +8e57f774d0e8302e1bf1834f84720bb1f9211ae3 \ No newline at end of file From f630cac06f40f4e52166c426befe77723cfbaaf7 Mon Sep 17 00:00:00 2001 From: Andrei Radulescu Date: Sun, 18 May 2025 15:57:45 +0300 Subject: [PATCH 265/477] modeld: replace CLANG=1 with CPU=1 (#35270) Replace CLANG=1 with CPU=1 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/andiradulescu/openpilot?shareId=XXXX-XXXX-XXXX-XXXX). --- selfdrive/modeld/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index 28d60b9e00..a6be5c15da 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -46,7 +46,7 @@ for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: elif arch == 'larch64': device_string = 'QCOM=1' elif arch == 'Darwin': - device_string = 'CLANG=1 IMAGE=0 JIT=2' + device_string = 'CPU=1 IMAGE=0 JIT=2' else: device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0 JIT=2' From 3aa9ddd3c76406796240494b8638c6bcee6441b7 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 18 May 2025 10:02:50 -0400 Subject: [PATCH 266/477] Revert "MADS: `wrongCarMode` alert only with selfdrive enable (#931)" (#932) This reverts commit 6d516a7704410b69a2707dfcb856eba5be519f81. --- sunnypilot/mads/mads.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index d5ce9354c1..456be01882 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -81,13 +81,6 @@ class ModularAssistiveDrivingSystem: return False - def get_wrong_car_mode(self, alert_only: bool) -> None: - if alert_only: - if self.events.has(EventName.wrongCarMode): - self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) - else: - self.events.remove(EventName.wrongCarMode) - def transition_paused_state(self): if self.state_machine.state != State.paused: self.events_sp.add(EventNameSP.silentLkasDisable) @@ -128,12 +121,11 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.manualRestart) selfdrive_enable_events = self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable) - set_speed_btns_enable = any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents) - - # wrongCarMode alert only or actively block control - self.get_wrong_car_mode(selfdrive_enable_events or set_speed_btns_enable) if selfdrive_enable_events: + if self.pedal_pressed_non_gas_pressed(CS): + self.events_sp.add(EventNameSP.pedalPressedAlertOnly) + if self.block_unified_engagement_mode(): self.events.remove(EventName.pcmEnable) self.events.remove(EventName.buttonEnable) @@ -168,6 +160,11 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.buttonCancel) self.events.remove(EventName.pedalPressed) self.events.remove(EventName.wrongCruiseMode) + if any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents): + if self.events.has(EventName.wrongCarMode): + self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) + else: + self.events.remove(EventName.wrongCarMode) def update(self, CS: structs.CarState): if not self.enabled_toggle: From a93e788401efd659fc18af848dcf563e5c89ce33 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 18 May 2025 10:14:03 -0400 Subject: [PATCH 267/477] Reapply "MADS: wrongCarMode alert only with selfdrive enable (#931)" (#933) * Revert "MADS: `wrongCarMode` alert only with selfdrive enable (#931)" This reverts commit 6d516a7704410b69a2707dfcb856eba5be519f81. * Reapply "MADS: `wrongCarMode` alert only with selfdrive enable (#931)" This reverts commit c9487597e4ae4aa10a7111d963c5933d472bc88e. * Reapply "MADS: `wrongCarMode` alert only with selfdrive enable (#931)" This reverts commit c9487597e4ae4aa10a7111d963c5933d472bc88e. --- sunnypilot/mads/mads.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 456be01882..e3ee7cd832 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -81,6 +81,13 @@ class ModularAssistiveDrivingSystem: return False + def get_wrong_car_mode(self, alert_only: bool) -> None: + if alert_only: + if self.events.has(EventName.wrongCarMode): + self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) + else: + self.events.remove(EventName.wrongCarMode) + def transition_paused_state(self): if self.state_machine.state != State.paused: self.events_sp.add(EventNameSP.silentLkasDisable) @@ -121,6 +128,10 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.manualRestart) selfdrive_enable_events = self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable) + set_speed_btns_enable = any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents) + + # wrongCarMode alert only or actively block control + self.get_wrong_car_mode(selfdrive_enable_events or set_speed_btns_enable) if selfdrive_enable_events: if self.pedal_pressed_non_gas_pressed(CS): @@ -160,11 +171,6 @@ class ModularAssistiveDrivingSystem: self.events.remove(EventName.buttonCancel) self.events.remove(EventName.pedalPressed) self.events.remove(EventName.wrongCruiseMode) - if any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents): - if self.events.has(EventName.wrongCarMode): - self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly) - else: - self.events.remove(EventName.wrongCarMode) def update(self, CS: structs.CarState): if not self.enabled_toggle: From ed0f8129955ddf727e937a01d240577814bcc6f2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 18 May 2025 16:21:20 -0700 Subject: [PATCH 268/477] system/ui: add SCALE env var (#35272) * ui: add SCALE env var * linting * cleaner --------- Co-authored-by: Cameron Clough --- system/ui/README.md | 1 + system/ui/lib/application.py | 37 +++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/system/ui/README.md b/system/ui/README.md index 5f47961a51..c71b77ab66 100644 --- a/system/ui/README.md +++ b/system/ui/README.md @@ -5,5 +5,6 @@ The user interfaces here are built with [raylib](https://www.raylib.com/). Quick start: * set `DEBUG_FPS=1` to show the FPS * set `STRICT_MODE=1` to kill the app if it drops too much below 60fps +* set `SCALE=1.5` to scale the entire UI by 1.5x * https://www.raylib.com/cheatsheet/cheatsheet.html * https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a2ea392784..83ec0f6ddd 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -15,6 +15,7 @@ FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1' +SCALE = float(os.getenv("SCALE", "1.0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE @@ -22,6 +23,7 @@ DEFAULT_TEXT_COLOR = rl.WHITE ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") + class FontWeight(IntEnum): THIN = 0 EXTRA_LIGHT = 1 @@ -39,6 +41,10 @@ class GuiApplication: self._fonts: dict[FontWeight, rl.Font] = {} self._width = width self._height = height + self._scale = SCALE + self._scaled_width = int(self._width * self._scale) + self._scaled_height = int(self._height * self._scale) + self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() @@ -62,14 +68,15 @@ class GuiApplication: flags |= rl.ConfigFlags.FLAG_VSYNC_HINT rl.set_config_flags(flags) - rl.init_window(self._width, self._height, title) + rl.init_window(self._scaled_width, self._scaled_height, title) + if self._scale != 1.0: + self._render_texture = rl.load_render_texture(self._width, self._height) rl.set_target_fps(fps) self._target_fps = fps self._set_styles() self._load_fonts() - def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" if cache_key in self._textures: @@ -80,7 +87,7 @@ class GuiApplication: self._textures[cache_key] = texture_obj return texture_obj - def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): + def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): """Load and resize a texture, storing it for later automatic unloading.""" image = rl.load_image(image_path) @@ -123,16 +130,32 @@ class GuiApplication: rl.unload_font(font) self._fonts = {} + if self._render_texture is not None: + rl.unload_render_texture(self._render_texture) + self._render_texture = None + rl.close_window() def render(self): try: while not (self._window_close_requested or rl.window_should_close()): - rl.begin_drawing() - rl.clear_background(rl.BLACK) + if self._render_texture: + rl.begin_texture_mode(self._render_texture) + rl.clear_background(rl.BLACK) + else: + rl.begin_drawing() + rl.clear_background(rl.BLACK) yield + if self._render_texture: + rl.end_texture_mode() + rl.begin_drawing() + rl.clear_background(rl.BLACK) + src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height)) + dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) + rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) + if DEBUG_FPS: rl.draw_fps(10, 10) @@ -141,7 +164,7 @@ class GuiApplication: except KeyboardInterrupt: pass - def font(self, font_weight: FontWeight=FontWeight.NORMAL): + def font(self, font_weight: FontWeight = FontWeight.NORMAL): return self._fonts[font_weight] @property @@ -163,7 +186,7 @@ class GuiApplication: "Inter-Bold.ttf", "Inter-ExtraBold.ttf", "Inter-Black.ttf", - ) + ) for index, font_file in enumerate(font_files): with as_file(FONT_DIR.joinpath(font_file)) as fspath: From e2569a8b322fbe105475e4d1336890fd79e2d522 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 00:27:35 +0100 Subject: [PATCH 269/477] Revert "system/ui: add SCALE env var (#35272)" This reverts commit ed0f8129955ddf727e937a01d240577814bcc6f2. --- system/ui/README.md | 1 - system/ui/lib/application.py | 37 +++++++----------------------------- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/system/ui/README.md b/system/ui/README.md index c71b77ab66..5f47961a51 100644 --- a/system/ui/README.md +++ b/system/ui/README.md @@ -5,6 +5,5 @@ The user interfaces here are built with [raylib](https://www.raylib.com/). Quick start: * set `DEBUG_FPS=1` to show the FPS * set `STRICT_MODE=1` to kill the app if it drops too much below 60fps -* set `SCALE=1.5` to scale the entire UI by 1.5x * https://www.raylib.com/cheatsheet/cheatsheet.html * https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 83ec0f6ddd..a2ea392784 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -15,7 +15,6 @@ FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1' -SCALE = float(os.getenv("SCALE", "1.0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE @@ -23,7 +22,6 @@ DEFAULT_TEXT_COLOR = rl.WHITE ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") - class FontWeight(IntEnum): THIN = 0 EXTRA_LIGHT = 1 @@ -41,10 +39,6 @@ class GuiApplication: self._fonts: dict[FontWeight, rl.Font] = {} self._width = width self._height = height - self._scale = SCALE - self._scaled_width = int(self._width * self._scale) - self._scaled_height = int(self._height * self._scale) - self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() @@ -68,15 +62,14 @@ class GuiApplication: flags |= rl.ConfigFlags.FLAG_VSYNC_HINT rl.set_config_flags(flags) - rl.init_window(self._scaled_width, self._scaled_height, title) - if self._scale != 1.0: - self._render_texture = rl.load_render_texture(self._width, self._height) + rl.init_window(self._width, self._height, title) rl.set_target_fps(fps) self._target_fps = fps self._set_styles() self._load_fonts() + def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" if cache_key in self._textures: @@ -87,7 +80,7 @@ class GuiApplication: self._textures[cache_key] = texture_obj return texture_obj - def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): + def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): """Load and resize a texture, storing it for later automatic unloading.""" image = rl.load_image(image_path) @@ -130,32 +123,16 @@ class GuiApplication: rl.unload_font(font) self._fonts = {} - if self._render_texture is not None: - rl.unload_render_texture(self._render_texture) - self._render_texture = None - rl.close_window() def render(self): try: while not (self._window_close_requested or rl.window_should_close()): - if self._render_texture: - rl.begin_texture_mode(self._render_texture) - rl.clear_background(rl.BLACK) - else: - rl.begin_drawing() - rl.clear_background(rl.BLACK) + rl.begin_drawing() + rl.clear_background(rl.BLACK) yield - if self._render_texture: - rl.end_texture_mode() - rl.begin_drawing() - rl.clear_background(rl.BLACK) - src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height)) - dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) - rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) - if DEBUG_FPS: rl.draw_fps(10, 10) @@ -164,7 +141,7 @@ class GuiApplication: except KeyboardInterrupt: pass - def font(self, font_weight: FontWeight = FontWeight.NORMAL): + def font(self, font_weight: FontWeight=FontWeight.NORMAL): return self._fonts[font_weight] @property @@ -186,7 +163,7 @@ class GuiApplication: "Inter-Bold.ttf", "Inter-ExtraBold.ttf", "Inter-Black.ttf", - ) + ) for index, font_file in enumerate(font_files): with as_file(FONT_DIR.joinpath(font_file)) as fspath: From 15c7d556f61d9dd61a420a8d1f6e165b3d3db184 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 00:33:52 +0100 Subject: [PATCH 270/477] Reapply "system/ui: add SCALE env var (#35272)" (#35273) * Reapply "system/ui: add SCALE env var (#35272)" This reverts commit e2569a8b322fbe105475e4d1336890fd79e2d522. * set mouse scale factor --- system/ui/README.md | 1 + system/ui/lib/application.py | 38 +++++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/system/ui/README.md b/system/ui/README.md index 5f47961a51..c71b77ab66 100644 --- a/system/ui/README.md +++ b/system/ui/README.md @@ -5,5 +5,6 @@ The user interfaces here are built with [raylib](https://www.raylib.com/). Quick start: * set `DEBUG_FPS=1` to show the FPS * set `STRICT_MODE=1` to kill the app if it drops too much below 60fps +* set `SCALE=1.5` to scale the entire UI by 1.5x * https://www.raylib.com/cheatsheet/cheatsheet.html * https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a2ea392784..19f01ca6ac 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -15,6 +15,7 @@ FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1' +SCALE = float(os.getenv("SCALE", "1.0")) DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_COLOR = rl.WHITE @@ -22,6 +23,7 @@ DEFAULT_TEXT_COLOR = rl.WHITE ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") FONT_DIR = ASSETS_DIR.joinpath("fonts") + class FontWeight(IntEnum): THIN = 0 EXTRA_LIGHT = 1 @@ -39,6 +41,10 @@ class GuiApplication: self._fonts: dict[FontWeight, rl.Font] = {} self._width = width self._height = height + self._scale = SCALE + self._scaled_width = int(self._width * self._scale) + self._scaled_height = int(self._height * self._scale) + self._render_texture: rl.RenderTexture | None = None self._textures: dict[str, rl.Texture] = {} self._target_fps: int = DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() @@ -62,14 +68,16 @@ class GuiApplication: flags |= rl.ConfigFlags.FLAG_VSYNC_HINT rl.set_config_flags(flags) - rl.init_window(self._width, self._height, title) + rl.init_window(self._scaled_width, self._scaled_height, title) + if self._scale != 1.0: + rl.set_mouse_scale(1 / self._scale, 1 / self._scale) + self._render_texture = rl.load_render_texture(self._width, self._height) rl.set_target_fps(fps) self._target_fps = fps self._set_styles() self._load_fonts() - def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" if cache_key in self._textures: @@ -80,7 +88,7 @@ class GuiApplication: self._textures[cache_key] = texture_obj return texture_obj - def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): + def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): """Load and resize a texture, storing it for later automatic unloading.""" image = rl.load_image(image_path) @@ -123,16 +131,32 @@ class GuiApplication: rl.unload_font(font) self._fonts = {} + if self._render_texture is not None: + rl.unload_render_texture(self._render_texture) + self._render_texture = None + rl.close_window() def render(self): try: while not (self._window_close_requested or rl.window_should_close()): - rl.begin_drawing() - rl.clear_background(rl.BLACK) + if self._render_texture: + rl.begin_texture_mode(self._render_texture) + rl.clear_background(rl.BLACK) + else: + rl.begin_drawing() + rl.clear_background(rl.BLACK) yield + if self._render_texture: + rl.end_texture_mode() + rl.begin_drawing() + rl.clear_background(rl.BLACK) + src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height)) + dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) + rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) + if DEBUG_FPS: rl.draw_fps(10, 10) @@ -141,7 +165,7 @@ class GuiApplication: except KeyboardInterrupt: pass - def font(self, font_weight: FontWeight=FontWeight.NORMAL): + def font(self, font_weight: FontWeight = FontWeight.NORMAL): return self._fonts[font_weight] @property @@ -163,7 +187,7 @@ class GuiApplication: "Inter-Bold.ttf", "Inter-ExtraBold.ttf", "Inter-Black.ttf", - ) + ) for index, font_file in enumerate(font_files): with as_file(FONT_DIR.joinpath(font_file)) as fspath: From 645bd391b793f343822c4eb5e2ded225019674b6 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 18 May 2025 16:57:56 -0700 Subject: [PATCH 271/477] readme: add sgo to branch guide --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 77002481d3..b0248b0f54 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

openpilot is an operating system for robotics.
- Currently, it upgrades the driver assistance system in 275+ supported cars. + Currently, it upgrades the driver assistance system in 300+ supported cars.

@@ -57,6 +57,7 @@ We have detailed instructions for [how to install the harness and device in a ca | `release3-staging` | openpilot-test.comma.ai | This is the staging branch for releases. Use it to get new releases slightly early. | | `nightly` | openpilot-nightly.comma.ai | This is the bleeding edge development branch. Do not expect this to be stable. | | `nightly-dev` | installer.comma.ai/commaai/nightly-dev | Same as nightly, but includes experimental development features for some cars. | +| `secretgoodopenpilot` | installer.comma.ai/commaai/secretgoodopenpilot | This is a preview branch from the autonomy team where new driving models get merged earlier than master. | To start developing openpilot ------ @@ -83,8 +84,8 @@ Safety and Testing * panda has additional hardware-in-the-loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile). * We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes. -Licensing ------- +
+MIT Licensed openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified. @@ -93,9 +94,10 @@ Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and i **THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT. YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS. NO WARRANTY EXPRESSED OR IMPLIED.** +
-User Data and comma Account ------- +
+User Data and comma Account By default, openpilot uploads the driving data to our servers. You can also access your data through [comma connect](https://connect.comma.ai/). We use your data to train better models and improve openpilot for everyone. @@ -105,3 +107,4 @@ openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sen The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded. By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data. +
From d7bf309eca1ae078d064d3587b0c9ae74a6ff2a1 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 00:58:33 +0100 Subject: [PATCH 272/477] system/ui: update keyboard enter key (#35275) * system/ui: update keyboard enter key - use arrow-right icon - use primary color * format --- system/ui/widgets/keyboard.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index aabd664171..ec6a70fb0a 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,13 +1,13 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight -from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label # Constants for special keys CONTENT_MARGIN = 50 BACKSPACE_KEY = "<-" -ENTER_KEY = "Enter" +ENTER_KEY = "->" SPACE_KEY = " " SHIFT_KEY = "↑" SHIFT_DOWN_KEY = "↓" @@ -59,6 +59,7 @@ class Keyboard: BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 60, 60), SHIFT_KEY: gui_app.texture("icons/shift.png", 60, 60), SHIFT_DOWN_KEY: gui_app.texture("icons/arrow-down.png", 60, 60), + ENTER_KEY: gui_app.texture("icons/arrow-right.png", 60, 60), } @property @@ -103,7 +104,7 @@ class Keyboard: result = -1 if key in self._key_icons: texture = self._key_icons[key] - result = gui_button(key_rect, "", icon=texture, is_enabled=is_enabled) + result = gui_button(key_rect, "", icon=texture, button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL, is_enabled=is_enabled) else: result = gui_button(key_rect, key, is_enabled=is_enabled) From f1ba6c389b9f545cb40a0c72e5851e7737993a1c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 18 May 2025 16:59:53 -0700 Subject: [PATCH 273/477] quick repo garbage collection (#35274) * start gc * lil more * debug * no sound * add back --- SConstruct | 12 +------ common/ffi_wrapper.py | 8 ----- common/filter_simple.py | 1 - pyproject.toml | 2 +- selfdrive/debug/{ => car}/clear_dtc.py | 0 .../{ => car}/hyundai_enable_radar_points.py | 0 .../debug/{ => car}/toyota_eps_factor.py | 0 selfdrive/debug/{ => car}/vw_mqb_config.py | 0 selfdrive/debug/measure_modeld_packet_drop.py | 33 ------------------- selfdrive/debug/show_matching_cars.py | 20 ----------- selfdrive/ui/qt/python_helpers.py | 5 ++- system/athena/athenad.py | 2 +- system/camerad/{snapshot => }/snapshot.py | 0 system/camerad/snapshot/__init__.py | 0 system/camerad/test/check_skips.py | 27 --------------- .../test/get_thumbnails_for_segment.py | 24 -------------- system/camerad/test/test_exposure.py | 2 +- 17 files changed, 8 insertions(+), 128 deletions(-) delete mode 100644 common/ffi_wrapper.py rename selfdrive/debug/{ => car}/clear_dtc.py (100%) rename selfdrive/debug/{ => car}/hyundai_enable_radar_points.py (100%) rename selfdrive/debug/{ => car}/toyota_eps_factor.py (100%) rename selfdrive/debug/{ => car}/vw_mqb_config.py (100%) delete mode 100755 selfdrive/debug/measure_modeld_packet_drop.py delete mode 100755 selfdrive/debug/show_matching_cars.py rename system/camerad/{snapshot => }/snapshot.py (100%) delete mode 100644 system/camerad/snapshot/__init__.py delete mode 100755 system/camerad/test/check_skips.py delete mode 100755 system/camerad/test/get_thumbnails_for_segment.py diff --git a/SConstruct b/SConstruct index c40f118985..d5597a70fe 100644 --- a/SConstruct +++ b/SConstruct @@ -55,11 +55,6 @@ AddOption('--external-sconscript', dest='external_sconscript', help='add an external SConscript to the build') -AddOption('--pc-thneed', - action='store_true', - dest='pc_thneed', - help='use thneed on pc') - AddOption('--mutation', action='store_true', help='generate mutation-ready code') @@ -288,12 +283,7 @@ else: elif arch != "Darwin": qt_libs += ["GL"] qt_env['QT3DIR'] = qt_env['QTDIR'] - -# compatibility for older SCons versions -try: - qt_env.Tool('qt3') -except SCons.Errors.UserError: - qt_env.Tool('qt') +qt_env.Tool('qt3') qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"] qt_flags = [ diff --git a/common/ffi_wrapper.py b/common/ffi_wrapper.py deleted file mode 100644 index 01741c6f42..0000000000 --- a/common/ffi_wrapper.py +++ /dev/null @@ -1,8 +0,0 @@ -import platform - - -def suffix(): - if platform.system() == "Darwin": - return ".dylib" - else: - return ".so" diff --git a/common/filter_simple.py b/common/filter_simple.py index 0ec7a51562..9ea6fe3070 100644 --- a/common/filter_simple.py +++ b/common/filter_simple.py @@ -1,5 +1,4 @@ class FirstOrderFilter: - # first order filter def __init__(self, x0, rc, dt, initialized=True): self.x = x0 self.dt = dt diff --git a/pyproject.toml b/pyproject.toml index 20bfebd938..464b605f86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,7 +123,7 @@ tools = [ ] [project.urls] -Homepage = "https://comma.ai" +Homepage = "https://github.com/commaai/openpilot" [build-system] requires = ["hatchling"] diff --git a/selfdrive/debug/clear_dtc.py b/selfdrive/debug/car/clear_dtc.py similarity index 100% rename from selfdrive/debug/clear_dtc.py rename to selfdrive/debug/car/clear_dtc.py diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/car/hyundai_enable_radar_points.py similarity index 100% rename from selfdrive/debug/hyundai_enable_radar_points.py rename to selfdrive/debug/car/hyundai_enable_radar_points.py diff --git a/selfdrive/debug/toyota_eps_factor.py b/selfdrive/debug/car/toyota_eps_factor.py similarity index 100% rename from selfdrive/debug/toyota_eps_factor.py rename to selfdrive/debug/car/toyota_eps_factor.py diff --git a/selfdrive/debug/vw_mqb_config.py b/selfdrive/debug/car/vw_mqb_config.py similarity index 100% rename from selfdrive/debug/vw_mqb_config.py rename to selfdrive/debug/car/vw_mqb_config.py diff --git a/selfdrive/debug/measure_modeld_packet_drop.py b/selfdrive/debug/measure_modeld_packet_drop.py deleted file mode 100755 index 9814942ce2..0000000000 --- a/selfdrive/debug/measure_modeld_packet_drop.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -import cereal.messaging as messaging - -if __name__ == "__main__": - modeld_sock = messaging.sub_sock("modelV2") - - last_frame_id = None - start_t: int | None = None - frame_cnt = 0 - dropped = 0 - - while True: - m = messaging.recv_one(modeld_sock) - if m is None: - continue - - frame_id = m.modelV2.frameId - t = m.logMonoTime / 1e9 - frame_cnt += 1 - - if start_t is None: - start_t = t - last_frame_id = frame_id - continue - - d_frame = frame_id - last_frame_id - dropped += d_frame - 1 - - expected_num_frames = int((t - start_t) * 20) - frame_drop = 100 * (1 - (expected_num_frames / frame_cnt)) - print(f"Num dropped {dropped}, Drop compared to 20Hz: {frame_drop:.2f}%") - - last_frame_id = frame_id diff --git a/selfdrive/debug/show_matching_cars.py b/selfdrive/debug/show_matching_cars.py deleted file mode 100755 index bc13772977..0000000000 --- a/selfdrive/debug/show_matching_cars.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 -from opendbc.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars -import cereal.messaging as messaging - - -# rav4 2019 and corolla tss2 -fingerprint = {896: 8, 898: 8, 900: 6, 976: 1, 1541: 8, 902: 6, 905: 8, 810: 2, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1552: 8, 1553: 8, 1556: 8, 1571: 8, 921: 8, 1056: 8, 544: 4, 1570: 8, 1059: 1, 36: 8, 37: 8, 550: 8, 935: 8, 552: 4, 170: 8, 812: 8, 944: 8, 945: 8, 562: 6, 180: 8, 1077: 8, 951: 8, 1592: 8, 1076: 8, 186: 4, 955: 8, 956: 8, 1001: 8, 705: 8, 452: 8, 1788: 8, 464: 8, 824: 8, 466: 8, 467: 8, 761: 8, 728: 8, 1572: 8, 1114: 8, 933: 8, 800: 8, 608: 8, 865: 8, 610: 8, 1595: 8, 934: 8, 998: 5, 1745: 8, 1000: 8, 764: 8, 1002: 8, 999: 7, 1789: 8, 1649: 8, 1779: 8, 1568: 8, 1017: 8, 1786: 8, 1787: 8, 1020: 8, 426: 6, 1279: 8} # noqa: E501 - -candidate_cars = all_legacy_fingerprint_cars() - - -for addr, l in fingerprint.items(): - dat = messaging.new_message('can', 1) - - msg = dat.can[0] - msg.address = addr - msg.dat = " " * l - - candidate_cars = eliminate_incompatible_cars(msg, candidate_cars) - print(candidate_cars) diff --git a/selfdrive/ui/qt/python_helpers.py b/selfdrive/ui/qt/python_helpers.py index 88c36290d9..1f6d43f309 100644 --- a/selfdrive/ui/qt/python_helpers.py +++ b/selfdrive/ui/qt/python_helpers.py @@ -1,11 +1,14 @@ import os +import platform from cffi import FFI import sip -from openpilot.common.ffi_wrapper import suffix from openpilot.common.basedir import BASEDIR +def suffix(): + return ".dylib" if platform.system() == "Darwin" else ".so" + def get_ffi(): lib = os.path.join(BASEDIR, "selfdrive", "ui", "qt", "libpython_helpers" + suffix()) diff --git a/system/athena/athenad.py b/system/athena/athenad.py index fb546fa40f..5a02d45c43 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -546,7 +546,7 @@ def getNetworks(): @dispatcher.add_method def takeSnapshot() -> str | dict[str, str] | None: - from openpilot.system.camerad.snapshot.snapshot import jpeg_write, snapshot + from openpilot.system.camerad.snapshot import jpeg_write, snapshot ret = snapshot() if ret is not None: def b64jpeg(x): diff --git a/system/camerad/snapshot/snapshot.py b/system/camerad/snapshot.py similarity index 100% rename from system/camerad/snapshot/snapshot.py rename to system/camerad/snapshot.py diff --git a/system/camerad/snapshot/__init__.py b/system/camerad/snapshot/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/system/camerad/test/check_skips.py b/system/camerad/test/check_skips.py deleted file mode 100755 index 0814ce44ff..0000000000 --- a/system/camerad/test/check_skips.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore -import cereal.messaging as messaging - -all_sockets = ['roadCameraState', 'driverCameraState', 'wideRoadCameraState'] -prev_id = [None,None,None] -this_id = [None,None,None] -dt = [None,None,None] -num_skipped = [0,0,0] - -if __name__ == "__main__": - sm = messaging.SubMaster(all_sockets) - while True: - sm.update() - - for i in range(len(all_sockets)): - if not sm.updated[all_sockets[i]]: - continue - this_id[i] = sm[all_sockets[i]].frameId - if prev_id[i] is None: - prev_id[i] = this_id[i] - continue - dt[i] = this_id[i] - prev_id[i] - if dt[i] != 1: - num_skipped[i] += dt[i] - 1 - print(all_sockets[i] ,dt[i] - 1, num_skipped[i]) - prev_id[i] = this_id[i] diff --git a/system/camerad/test/get_thumbnails_for_segment.py b/system/camerad/test/get_thumbnails_for_segment.py deleted file mode 100755 index 21409f398d..0000000000 --- a/system/camerad/test/get_thumbnails_for_segment.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import os -from tqdm import tqdm - -from openpilot.tools.lib.logreader import LogReader - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("route", help="The route name") - args = parser.parse_args() - - out_path = os.path.join("jpegs", f"{args.route.replace('|', '_').replace('/', '_')}") - os.makedirs(out_path, exist_ok=True) - - lr = LogReader(args.route) - - for msg in tqdm(lr): - if msg.which() == 'thumbnail': - with open(os.path.join(out_path, f"{msg.thumbnail.frameId}.jpg"), 'wb') as f: - f.write(msg.thumbnail.thumbnail) - elif msg.which() == 'navThumbnail': - with open(os.path.join(out_path, f"nav_{msg.navThumbnail.frameId}.jpg"), 'wb') as f: - f.write(msg.navThumbnail.thumbnail) diff --git a/system/camerad/test/test_exposure.py b/system/camerad/test/test_exposure.py index 97f03ed182..f431c03410 100644 --- a/system/camerad/test/test_exposure.py +++ b/system/camerad/test/test_exposure.py @@ -3,7 +3,7 @@ import numpy as np import pytest from openpilot.selfdrive.test.helpers import with_processes -from openpilot.system.camerad.snapshot.snapshot import get_snapshots +from openpilot.system.camerad.snapshot import get_snapshots TEST_TIME = 45 REPEAT = 5 From 66409866223afca81b3ec98636f6c61e9c5919e8 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 01:31:38 +0100 Subject: [PATCH 274/477] system/ui: increase keyboard key font/icon size (#35276) --- system/ui/widgets/keyboard.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index ec6a70fb0a..bceffad943 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -4,6 +4,8 @@ from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label +KEY_FONT_SIZE = 96 + # Constants for special keys CONTENT_MARGIN = 50 BACKSPACE_KEY = "<-" @@ -56,10 +58,10 @@ class Keyboard: self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) self._key_icons = { - BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 60, 60), - SHIFT_KEY: gui_app.texture("icons/shift.png", 60, 60), - SHIFT_DOWN_KEY: gui_app.texture("icons/arrow-down.png", 60, 60), - ENTER_KEY: gui_app.texture("icons/arrow-right.png", 60, 60), + BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 80, 80), + SHIFT_KEY: gui_app.texture("icons/shift.png", 80, 80), + SHIFT_DOWN_KEY: gui_app.texture("icons/arrow-down.png", 80, 80), + ENTER_KEY: gui_app.texture("icons/arrow-right.png", 80, 80), } @property @@ -106,7 +108,7 @@ class Keyboard: texture = self._key_icons[key] result = gui_button(key_rect, "", icon=texture, button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL, is_enabled=is_enabled) else: - result = gui_button(key_rect, key, is_enabled=is_enabled) + result = gui_button(key_rect, key, KEY_FONT_SIZE, is_enabled=is_enabled) if result: if key == ENTER_KEY: From ebe56410d3133957c27219e0666e6304abb5cd91 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 19 May 2025 01:25:11 -0400 Subject: [PATCH 275/477] MADS: Steering Mode on Brake Pedal Press (#924) * MADS: Steering Mode on Brake Pedal Press * bump * bump * descriptions * bump * bump * no tesla or rivian yet * codecov v5 * Revert "codecov v5" This reverts commit a347e3fb27c0fbf2510b69fe8148b254fa93b8de. * cleanup * refactor description * sync name * fix * make sure we don't allow if brake was already being pressed * fix * diff * fix description * in another PR --- common/params_keys.h | 2 +- opendbc_repo | 2 +- selfdrive/pandad/pandad.cc | 4 +- .../offroad/settings/lateral/mads_settings.cc | 27 ++++++----- .../offroad/settings/lateral/mads_settings.h | 46 ++++++++++++++++++- sunnypilot/mads/helpers.py | 21 +++++++-- sunnypilot/mads/mads.py | 17 +++++-- system/manager/manager.py | 2 +- 8 files changed, 97 insertions(+), 24 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 97f23d5108..d3a9ea7d6d 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -138,7 +138,7 @@ inline static std::unordered_map keys = { // MADS params {"Mads", PERSISTENT | BACKUP}, {"MadsMainCruiseAllowed", PERSISTENT | BACKUP}, - {"MadsPauseLateralOnBrake", PERSISTENT | BACKUP}, + {"MadsSteeringMode", PERSISTENT | BACKUP}, {"MadsUnifiedEngagementMode", PERSISTENT | BACKUP}, // Model Manager params diff --git a/opendbc_repo b/opendbc_repo index 36461557c4..a051754777 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 36461557c47176b16a081ea40195bc327a34d97f +Subproject commit a051754777785cc93eb0be35b081d19f7fbce595 diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index d3dbba8182..1b4defd2b9 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -41,7 +41,7 @@ #define CUTOFF_IL 400 #define SATURATE_IL 1000 -#define ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE 2048 +#define ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE 2048 ExitHandler do_exit; @@ -57,7 +57,7 @@ bool check_all_connected(const std::vector &pandas) { bool process_mads_heartbeat(SubMaster *sm) { const int &alt_exp = (*sm)["carParams"].getCarParams().getAlternativeExperience(); - const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE) != 0; + const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE) != 0; const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads(); const bool heartbeat_type = disengage_lateral_on_brake ? mads.getActive() : mads.getEnabled(); diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc index fa0f7f8bd4..97b78399d3 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc @@ -39,17 +39,12 @@ MadsSettings::MadsSettings(QWidget *parent) : QWidget(parent) { ""); list->addItem(madsUnifiedEngagementModeToggle); - // Pause Lateral On Brake - std::vector lateral_on_brake_texts{tr("Remain Active"), tr("Pause Steering")}; - madsPauseLateralOnBrake = new ButtonParamControl( - "MadsPauseLateralOnBrake", - tr("Steering Mode After Braking"), - tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot.\n\n" - "Remain Active: ALC will remain active even after the brake pedal is pressed.\nPause Steering: ALC will be paused after the brake pedal is manually pressed."), - "", - lateral_on_brake_texts, - 500); - list->addItem(madsPauseLateralOnBrake); + // Steering Mode On Brake + madsSteeringMode = new ButtonParamControl("MadsSteeringMode", tr("Steering Mode on Brake Pedal"), "", "", madsSteeringModeTexts(), 500); + QObject::connect(madsSteeringMode, &ButtonParamControl::buttonToggled, [=] { + updateToggles(offroad); + }); + list->addItem(madsSteeringMode); QObject::connect(uiState(), &UIState::offroadTransition, this, &MadsSettings::updateToggles); @@ -61,7 +56,15 @@ void MadsSettings::showEvent(QShowEvent *event) { } void MadsSettings::updateToggles(bool _offroad) { - madsPauseLateralOnBrake->setEnabled(_offroad); + auto mads_steering_mode_param = std::atoi(params.get("MadsSteeringMode").c_str()); + + auto steering_mode = static_cast( + std::clamp(mads_steering_mode_param, static_cast(MadsSteeringMode::REMAIN_ACTIVE), static_cast(MadsSteeringMode::DISENGAGE)) + ); + + madsSteeringMode->setEnabled(_offroad); + madsSteeringMode->setDescription(madsSteeringModeDescription(steering_mode)); + madsSteeringMode->showDescription(); offroad = _offroad; } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h index fccb4cbb84..07ca07834d 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h @@ -12,6 +12,18 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +enum class MadsSteeringMode { + REMAIN_ACTIVE = 0, + PAUSE = 1, + DISENGAGE = 2, +}; + +struct MadsSteeringModeOption { + MadsSteeringMode mode; + QString display_text; + QString description; +}; + class MadsSettings : public QWidget { Q_OBJECT @@ -32,5 +44,37 @@ private: ParamControl *madsMainCruiseToggle; ParamControl *madsUnifiedEngagementModeToggle; - ButtonParamControl *madsPauseLateralOnBrake; + ButtonParamControl *madsSteeringMode; + + static const std::vector &madsSteeringModeOptions() { + static const std::vector options = { + {MadsSteeringMode::REMAIN_ACTIVE, tr("Remain Active"), tr("Remain Active: ALC will remain active when the brake pedal is pressed.")}, + {MadsSteeringMode::PAUSE, tr("Pause"), tr("Pause: ALC will pause when the brake pedal is pressed.")}, + {MadsSteeringMode::DISENGAGE, tr("Disengage"), tr("Disengage: ALC will disengage when the brake pedal is pressed.")}, + }; + return options; + } + + static std::vector madsSteeringModeTexts() { + std::vector texts; + for (const auto& option : madsSteeringModeOptions()) { + texts.push_back(option.display_text); + } + return texts; + } + + static QString madsSteeringModeDescription(const MadsSteeringMode mode) { + QString base_desc = tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot."); + QString result = base_desc + "

"; + + for (const auto& option : madsSteeringModeOptions()) { + QString desc = option.description; + if (option.mode == mode) { + desc = "" + desc + ""; + } + result += desc + "
"; + } + + return result; + } }; diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py index 831a75699e..5f7f9840e2 100644 --- a/sunnypilot/mads/helpers.py +++ b/sunnypilot/mads/helpers.py @@ -11,15 +11,30 @@ from opendbc.safety import ALTERNATIVE_EXPERIENCE from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP +class MadsSteeringModeOnBrake: + REMAIN_ACTIVE = 0 + PAUSE = 1 + DISENGAGE = 2 + + +def read_steering_mode_param(params: Params): + try: + return int(params.get("MadsSteeringMode")) + except (ValueError, TypeError): + return MadsSteeringModeOnBrake.REMAIN_ACTIVE + + def set_alternative_experience(CP: structs.CarParams, params: Params): enabled = params.get_bool("Mads") - pause_lateral_on_brake = params.get_bool("MadsPauseLateralOnBrake") + steering_mode = read_steering_mode_param(params) if enabled: CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS - if pause_lateral_on_brake: - CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISENGAGE_LATERAL_ON_BRAKE + if steering_mode == MadsSteeringModeOnBrake.DISENGAGE: + CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_DISENGAGE_LATERAL_ON_BRAKE + elif steering_mode == MadsSteeringModeOnBrake.PAUSE: + CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_PAUSE_LATERAL_ON_BRAKE def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params): diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index e3ee7cd832..38d38230ab 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -10,6 +10,7 @@ from cereal import log, custom from opendbc.car import structs from opendbc.car.hyundai.values import HyundaiFlags from opendbc.safety import ALTERNATIVE_EXPERIENCE +from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState @@ -46,7 +47,7 @@ class ModularAssistiveDrivingSystem: # read params on init self.enabled_toggle = self.params.get_bool("Mads") self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed") - self.pause_lateral_on_brake_toggle = self.params.get_bool("MadsPauseLateralOnBrake") + self.steering_mode_on_brake = read_steering_mode_param(self.params) self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode") def read_params(self): @@ -60,7 +61,7 @@ class ModularAssistiveDrivingSystem: return False def should_silent_lkas_enable(self, CS: structs.CarState) -> bool: - if self.pause_lateral_on_brake_toggle and self.pedal_pressed_non_gas_pressed(CS): + if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE and self.pedal_pressed_non_gas_pressed(CS): return False if self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT): @@ -117,7 +118,7 @@ class ModularAssistiveDrivingSystem: self.replace_event(EventName.parkBrake, EventNameSP.silentParkBrake) self.transition_paused_state() - if self.pause_lateral_on_brake_toggle: + if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE: if self.pedal_pressed_non_gas_pressed(CS): self.transition_paused_state() @@ -163,6 +164,16 @@ class ModularAssistiveDrivingSystem: if self.selfdrive.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasDisable) + if self.steering_mode_on_brake == MadsSteeringModeOnBrake.DISENGAGE: + if self.pedal_pressed_non_gas_pressed(CS): + if self.enabled: + self.events_sp.add(EventNameSP.lkasDisable) + else: + # block lkasEnable if being sent, then send pedalPressedAlertOnly event + if self.events_sp.contains(EventNameSP.lkasEnable): + self.events_sp.remove(EventNameSP.lkasEnable) + self.events_sp.add(EventNameSP.pedalPressedAlertOnly) + if self.should_silent_lkas_enable(CS): if self.state_machine.state == State.paused: self.events_sp.add(EventNameSP.silentLkasEnable) diff --git a/system/manager/manager.py b/system/manager/manager.py index 90df415a8c..71f2bc525a 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -48,7 +48,7 @@ def manager_init() -> None: ("HyundaiLongitudinalTuning", "0"), ("Mads", "1"), ("MadsMainCruiseAllowed", "1"), - ("MadsPauseLateralOnBrake", "0"), + ("MadsSteeringMode", "0"), ("MadsUnifiedEngagementMode", "1"), ("MaxTimeOffroad", "1800"), ("ModelManager_LastSyncTime", "0"), From 0c013f61863230e22e8491cfd8945c600229e691 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 11:28:04 +0100 Subject: [PATCH 276/477] system/ui: add caps lock to keyboard (#35277) * add new icons shift-fill and capslock-fill, rm capslock * SHIFT_KEY_ON, SHIFT_KEY_OFF * capslock * rm arrow-down * a lot simpler * only one * just use time * layout name * rename shift * CONSTANT --- selfdrive/assets/icons/arrow-down.png | 3 -- selfdrive/assets/icons/capslock-fill.png | 3 ++ selfdrive/assets/icons/capslock.png | 3 -- selfdrive/assets/icons/shift-fill.png | 3 ++ selfdrive/assets/prep-svg.sh | 4 +- system/ui/widgets/keyboard.py | 53 +++++++++++++++++------- 6 files changed, 45 insertions(+), 24 deletions(-) delete mode 100644 selfdrive/assets/icons/arrow-down.png create mode 100644 selfdrive/assets/icons/capslock-fill.png delete mode 100644 selfdrive/assets/icons/capslock.png create mode 100644 selfdrive/assets/icons/shift-fill.png diff --git a/selfdrive/assets/icons/arrow-down.png b/selfdrive/assets/icons/arrow-down.png deleted file mode 100644 index 834c1cb8b9..0000000000 --- a/selfdrive/assets/icons/arrow-down.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7e12a3cf36fdef107d237457e20b44e16320414f9de8a1791aff1ec5fd85ccef -size 2390 diff --git a/selfdrive/assets/icons/capslock-fill.png b/selfdrive/assets/icons/capslock-fill.png new file mode 100644 index 0000000000..66854e78f2 --- /dev/null +++ b/selfdrive/assets/icons/capslock-fill.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6872a1047f1a534a037be7b1367640fe1bfb205a6e1c50420a2d1a946cda78ed +size 4397 diff --git a/selfdrive/assets/icons/capslock.png b/selfdrive/assets/icons/capslock.png deleted file mode 100644 index 61414d7230..0000000000 --- a/selfdrive/assets/icons/capslock.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c6621d000d9514df1d636e9953941eadec415ff950724f26a61e0fcbaa7a3818 -size 5403 diff --git a/selfdrive/assets/icons/shift-fill.png b/selfdrive/assets/icons/shift-fill.png new file mode 100644 index 0000000000..1ce02d5822 --- /dev/null +++ b/selfdrive/assets/icons/shift-fill.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e625ca991746abaaac375b095aa9a586601982232f4aae0fc2b17b2a524e9ce9 +size 3946 diff --git a/selfdrive/assets/prep-svg.sh b/selfdrive/assets/prep-svg.sh index d29ce3025d..2332cd25c5 100755 --- a/selfdrive/assets/prep-svg.sh +++ b/selfdrive/assets/prep-svg.sh @@ -6,11 +6,11 @@ ICONS_DIR="$DIR/icons" BOOTSTRAP_SVG="$DIR/../../third_party/bootstrap/bootstrap-icons.svg" ICON_IDS=( - arrow-down arrow-right backspace - capslock + capslock-fill shift + shift-fill ) ICON_FILL_COLOR="#fff" diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index bceffad943..5eebb49c83 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -1,3 +1,5 @@ +import time +from typing import Literal import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import ButtonStyle, gui_button @@ -5,30 +7,32 @@ from openpilot.system.ui.lib.inputbox import InputBox from openpilot.system.ui.lib.label import gui_label KEY_FONT_SIZE = 96 +DOUBLE_CLICK_THRESHOLD = 0.5 # seconds # Constants for special keys CONTENT_MARGIN = 50 BACKSPACE_KEY = "<-" ENTER_KEY = "->" SPACE_KEY = " " -SHIFT_KEY = "↑" -SHIFT_DOWN_KEY = "↓" +SHIFT_INACTIVE_KEY = "SHIFT_OFF" +SHIFT_ACTIVE_KEY = "SHIFT_ON" +CAPS_LOCK_KEY = "CAPS" NUMERIC_KEY = "123" SYMBOL_KEY = "#+=" ABC_KEY = "ABC" # Define keyboard layouts as a dictionary for easier access -keyboard_layouts = { +KEYBOARD_LAYOUTS = { "lowercase": [ ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], ["a", "s", "d", "f", "g", "h", "j", "k", "l"], - [SHIFT_KEY, "z", "x", "c", "v", "b", "n", "m", BACKSPACE_KEY], + [SHIFT_INACTIVE_KEY, "z", "x", "c", "v", "b", "n", "m", BACKSPACE_KEY], [NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY], ], "uppercase": [ ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], ["A", "S", "D", "F", "G", "H", "J", "K", "L"], - [SHIFT_DOWN_KEY, "Z", "X", "C", "V", "B", "N", "M", BACKSPACE_KEY], + [SHIFT_ACTIVE_KEY, "Z", "X", "C", "V", "B", "N", "M", BACKSPACE_KEY], [NUMERIC_KEY, "/", "-", SPACE_KEY, ".", ENTER_KEY], ], "numbers": [ @@ -48,7 +52,10 @@ keyboard_layouts = { class Keyboard: def __init__(self, max_text_size: int = 255, min_text_size: int = 0, password_mode: bool = False, show_password_toggle: bool = False): - self._layout = keyboard_layouts["lowercase"] + self._layout_name: Literal["lowercase", "uppercase", "numbers", "specials"] = "lowercase" + self._caps_lock = False + self._last_shift_press_time = 0 + self._max_text_size = max_text_size self._min_text_size = min_text_size self._input_box = InputBox(max_text_size) @@ -59,8 +66,9 @@ class Keyboard: self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) self._key_icons = { BACKSPACE_KEY: gui_app.texture("icons/backspace.png", 80, 80), - SHIFT_KEY: gui_app.texture("icons/shift.png", 80, 80), - SHIFT_DOWN_KEY: gui_app.texture("icons/arrow-down.png", 80, 80), + SHIFT_INACTIVE_KEY: gui_app.texture("icons/shift.png", 80, 80), + SHIFT_ACTIVE_KEY: gui_app.texture("icons/shift-fill.png", 80, 80), + CAPS_LOCK_KEY: gui_app.texture("icons/capslock-fill.png", 80, 80), ENTER_KEY: gui_app.texture("icons/arrow-right.png", 80, 80), } @@ -84,13 +92,15 @@ class Keyboard: input_box_rect = rl.Rectangle(rect.x + input_margin, rect.y + 160, rect.width - input_margin, 100) self._render_input_area(input_box_rect) + layout = KEYBOARD_LAYOUTS[self._layout_name] + h_space, v_space = 15, 15 row_y_start = rect.y + 300 # Starting Y position for the first row key_height = (rect.height - 300 - 3 * v_space) / 4 - key_max_width = (rect.width - (len(self._layout[2]) - 1) * h_space) / len(self._layout[2]) + key_max_width = (rect.width - (len(layout[2]) - 1) * h_space) / len(layout[2]) # Iterate over the rows of keys in the current layout - for row, keys in enumerate(self._layout): + for row, keys in enumerate(layout): key_width = min((rect.width - (180 if row == 1 else 0) - h_space * (len(keys) - 1)) / len(keys), key_max_width) start_x = rect.x + (90 if row == 1 else 0) @@ -105,6 +115,8 @@ class Keyboard: is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size result = -1 if key in self._key_icons: + if key == SHIFT_ACTIVE_KEY and self._caps_lock: + key = CAPS_LOCK_KEY texture = self._key_icons[key] result = gui_button(key_rect, "", icon=texture, button_style=ButtonStyle.PRIMARY if key == ENTER_KEY else ButtonStyle.NORMAL, is_enabled=is_enabled) else: @@ -148,18 +160,27 @@ class Keyboard: ) def handle_key_press(self, key): - if key in (SHIFT_DOWN_KEY, ABC_KEY): - self._layout = keyboard_layouts["lowercase"] - elif key == SHIFT_KEY: - self._layout = keyboard_layouts["uppercase"] + if key in (CAPS_LOCK_KEY, ABC_KEY): + self._caps_lock = False + self._layout_name = "lowercase" + elif key == SHIFT_INACTIVE_KEY: + self._last_shift_press_time = time.monotonic() + self._layout_name = "uppercase" + elif key == SHIFT_ACTIVE_KEY: + if time.monotonic() - self._last_shift_press_time < DOUBLE_CLICK_THRESHOLD: + self._caps_lock = True + else: + self._layout_name = "lowercase" elif key == NUMERIC_KEY: - self._layout = keyboard_layouts["numbers"] + self._layout_name = "numbers" elif key == SYMBOL_KEY: - self._layout = keyboard_layouts["specials"] + self._layout_name = "specials" elif key == BACKSPACE_KEY: self._input_box.delete_char_before_cursor() else: self._input_box.add_char_at_cursor(key) + if not self._caps_lock and self._layout_name == "uppercase": + self._layout_name = "lowercase" if __name__ == "__main__": From 9bbbff14d5f282833216124a39a0f629f79f569c Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 11:36:04 +0100 Subject: [PATCH 277/477] system/ui: don't reset on PC --- system/ui/reset.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/system/ui/reset.py b/system/ui/reset.py index 80a1c10ea8..20b689934b 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -5,6 +5,7 @@ import sys import threading from enum import IntEnum +from openpilot.system.hardware import PC from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label, gui_text_box @@ -31,7 +32,10 @@ class Reset: self.mode = mode self.reset_state = ResetState.NONE - def do_reset(self): + def _do_erase(self): + if PC: + return + # Best effort to wipe NVME os.system(f"sudo umount {NVME}") os.system(f"yes | sudo mkfs.ext4 {NVME}") @@ -48,7 +52,7 @@ class Reset: def start_reset(self): self.reset_state = ResetState.RESETTING - threading.Timer(0.1, self.do_reset).start() + threading.Timer(0.1, self._do_erase).start() def render(self, rect: rl.Rectangle): label_rect = rl.Rectangle(rect.x + 140, rect.y, rect.width - 280, 100) From 38c1bd096b5ec0f7bca5bed8700bb5899077b7a1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 19 May 2025 21:56:45 +0800 Subject: [PATCH 278/477] system/ui: Improve UI rendering smoothness for scaled display (#35280) improve smoothness --- system/ui/lib/application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 19f01ca6ac..8639a0bda5 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -72,6 +72,7 @@ class GuiApplication: if self._scale != 1.0: rl.set_mouse_scale(1 / self._scale, 1 / self._scale) self._render_texture = rl.load_render_texture(self._width, self._height) + rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) rl.set_target_fps(fps) self._target_fps = fps From c4d202c6bb77465dffc9aab037ccf2bc52491d7f Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 19 May 2025 11:49:44 -0400 Subject: [PATCH 279/477] ui: support selected button enabled for `ButtonParamControlSP` (#937) ui: support specific button enabled selections for `ButtonParamControlSP` Replaced setDisabledSelectedButton with setEnableSelectedButtons for improved flexibility. The new implementation allows enabling multiple buttons based on a given list and maintains clarity in handling button states. This enhances functionality and aligns with better code practices. --- selfdrive/ui/sunnypilot/qt/widgets/controls.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index 2a1bc20ac1..b6efd871d1 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -324,10 +324,11 @@ public: } } - void setDisabledSelectedButton(std::string val) { - int value = atoi(val.c_str()); + void setEnableSelectedButtons(bool enable, const std::vector& enabled_btns = {}) const { for (int i = 0; i < button_group->buttons().size(); i++) { - button_group->buttons()[i]->setEnabled(i != value); + // Enable the button if its index is in the enabled list + bool should_enable = std::find(enabled_btns.begin(), enabled_btns.end(), i) != enabled_btns.end(); + button_group->buttons()[i]->setEnabled(enable && should_enable); } } From 6eecb4f986df2e35386eabfec4cf3b4e3a4d534a Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 17:31:42 +0100 Subject: [PATCH 280/477] system/ui: network widget improvements (#35284) * larger font size for confirmation dialog * try this * forget btn color * text color * font size * caps * Revert "caps" This reverts commit a3e6cfbf053e46158036728cbcd9d8bd9f99d5e5. * too much * fixme * do that? * keyboard: reset state on clear --- system/ui/lib/button.py | 12 ++++++++++-- system/ui/widgets/confirm_dialog.py | 1 + system/ui/widgets/keyboard.py | 2 ++ system/ui/widgets/network.py | 8 ++++---- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index 2d58aee640..69a5da646e 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -8,6 +8,7 @@ class ButtonStyle(IntEnum): PRIMARY = 1 # For main actions DANGER = 2 # For critical actions, like reboot or delete TRANSPARENT = 3 # For buttons with transparent background and border + ACTION = 4 class TextAlignment(IntEnum): @@ -20,6 +21,8 @@ ICON_PADDING = 15 DEFAULT_BUTTON_FONT_SIZE = 60 BUTTON_ENABLED_TEXT_COLOR = rl.Color(228, 228, 228, 255) BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) +ACTION_BUTTON_FONT_SIZE = 48 +ACTION_BUTTON_TEXT_COLOR = rl.Color(0, 0, 0, 255) BUTTON_BACKGROUND_COLORS = { @@ -27,6 +30,7 @@ BUTTON_BACKGROUND_COLORS = { ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.ACTION: rl.Color(189, 189, 189, 255), } BUTTON_PRESSED_BACKGROUND_COLORS = { @@ -34,6 +38,7 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255), ButtonStyle.DANGER: rl.Color(255, 36, 36, 255), ButtonStyle.TRANSPARENT: rl.BLACK, + ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), } @@ -47,13 +52,16 @@ def gui_button( border_radius: int = 10, # Corner rounding in pixels text_alignment: TextAlignment = TextAlignment.CENTER, text_padding: int = 20, # Padding for left/right alignment - icon = None, + icon=None, ) -> int: result = 0 if button_style in (ButtonStyle.PRIMARY, ButtonStyle.DANGER) and not is_enabled: button_style = ButtonStyle.NORMAL + if button_style == ButtonStyle.ACTION and font_size == DEFAULT_BUTTON_FONT_SIZE: + font_size = ACTION_BUTTON_FONT_SIZE + # Set background color based on button type bg_color = BUTTON_BACKGROUND_COLORS[button_style] if is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect): @@ -105,7 +113,7 @@ def gui_button( # Draw the button text if any if text: - text_color = BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR + text_color = ACTION_BUTTON_TEXT_COLOR if button_style == ButtonStyle.ACTION else BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR rl.draw_text_ex(font, text, text_pos, font_size, 0, text_color) return result diff --git a/system/ui/widgets/confirm_dialog.py b/system/ui/widgets/confirm_dialog.py index 7a25682e27..f42220053b 100644 --- a/system/ui/widgets/confirm_dialog.py +++ b/system/ui/widgets/confirm_dialog.py @@ -34,6 +34,7 @@ def confirm_dialog(message: str, confirm_text: str, cancel_text: str = "Cancel") gui_text_box( text_rect, message, + font_size=88, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, ) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 5eebb49c83..85608c1ef5 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -77,6 +77,8 @@ class Keyboard: return self._input_box.text def clear(self): + self._layout_name = "lowercase" + self._caps_lock = False self._input_box.clear() def render(self, title: str, sub_title: str): diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 29d9a499da..32f8bcb4a4 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -3,7 +3,7 @@ from typing import Literal import pyray as rl from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.button import ButtonStyle, gui_button from openpilot.system.ui.lib.label import gui_label from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.wifi_manager import NetworkInfo, WifiManagerCallbacks, WifiManagerWrapper, SecurityType @@ -134,7 +134,7 @@ class WifiManagerUI: if status_text: status_text_rect = rl.Rectangle(security_icon_rect.x - 410, rect.y, 410, ITEM_HEIGHT) - rl.gui_label(status_text_rect, status_text) + gui_label(status_text_rect, status_text, font_size=48, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) else: # If the network is saved, show the "Forget" button if network.is_saved: @@ -143,7 +143,7 @@ class WifiManagerUI: self.btn_width, 80, ) - if isinstance(self.state, StateIdle) and gui_button(forget_btn_rect, "Forget") and clicked: + if isinstance(self.state, StateIdle) and gui_button(forget_btn_rect, "Forget", button_style=ButtonStyle.ACTION) and clicked: self.state = StateShowForgetConfirm(network) self._draw_status_icon(security_icon_rect, network) @@ -152,7 +152,7 @@ class WifiManagerUI: if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), ssid_rect) and clicked: if not network.is_saved: self.state = StateNeedsAuth(network) - else: + elif not network.is_connected: self.connect_to_network(network) def _draw_status_icon(self, rect, network: NetworkInfo): From a6eba52791cea780d70d0b8385a041d76c97df5d Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 19 May 2025 13:08:53 -0400 Subject: [PATCH 281/477] ui: Add utility function to check if a brand is in a list (#939) * ui: Add utility function to check if a brand is in a list Introduces `isBrandInList` to simplify checking if a given brand exists within a list of strings. This improves code clarity and avoids repetitive implementations for the same logic. * Revert "ui: Add utility function to check if a brand is in a list" This reverts commit 26cde3a73e59cda678c9e017536702de45c8d151. * ui: Add utility function to check if a brand is in a list --- selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h index 2fe511cc48..ff25d3b64f 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h @@ -12,6 +12,10 @@ #include "selfdrive/ui/qt/offroad/settings.h" +inline bool isBrandInList(const std::string &brand, const std::vector &list) { + return std::find(list.begin(), list.end(), brand) != list.end(); +} + class SettingsWindowSP : public SettingsWindow { Q_OBJECT From 41db0557e9ad3b865583bcb8e8e4ee686a6a4022 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 20 May 2025 01:56:25 +0800 Subject: [PATCH 282/477] system/ui: fix Wi-Fi conneciton flow for open networks (#35285) fix Wi-Fi conneciton flow for open networks --- system/ui/widgets/network.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 32f8bcb4a4..5877b3ff7a 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -150,21 +150,24 @@ class WifiManagerUI: self._draw_signal_strength_icon(signal_icon_rect, network) if isinstance(self.state, StateIdle) and rl.check_collision_point_rec(rl.get_mouse_position(), ssid_rect) and clicked: - if not network.is_saved: + if not network.is_saved and network.security_type != SecurityType.OPEN: self.state = StateNeedsAuth(network) elif not network.is_connected: self.connect_to_network(network) def _draw_status_icon(self, rect, network: NetworkInfo): """Draw the status icon based on network's connection state""" - icon_file = "" + icon_file = None if network.is_connected: icon_file = "icons/checkmark.png" elif network.security_type == SecurityType.UNSUPPORTED: icon_file = "icons/circled_slash.png" - else: + elif network.security_type != SecurityType.OPEN: icon_file = "icons/lock_closed.png" + if not icon_file: + return + texture = gui_app.texture(icon_file, ICON_SIZE, ICON_SIZE) icon_rect = rl.Vector2(rect.x, rect.y + (ICON_SIZE - texture.height) / 2) rl.draw_texture_v(texture, icon_rect, rl.WHITE) From d6e9df98e3cb73de7e0c0448dc04844776435e5b Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Mon, 19 May 2025 19:18:48 +0100 Subject: [PATCH 283/477] system/ui: WPA3 is unsupported (#35286) --- system/ui/lib/wifi_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 4ac7466cb7..96726bd999 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -596,7 +596,8 @@ class WifiManager: if flags == 0 and not (wpa_flags or rsn_flags): return SecurityType.OPEN if rsn_flags & 0x200: # SAE (WPA3 Personal) - return SecurityType.WPA3 + # TODO: support WPA3 + return SecurityType.UNSUPPORTED if rsn_flags: # RSN indicates WPA2 or higher return SecurityType.WPA2 if wpa_flags: # WPA flags indicate WPA From ddfb7420ca49243d1d8b13635883c167b6ddedd6 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 19 May 2025 14:25:26 -0400 Subject: [PATCH 284/477] MADS: prerequisite for partial platform support (#938) * MADS: prerequisite for partial platform support * ui: support specific button enabled selections for `ButtonParamControlSP` Replaced setDisabledSelectedButton with setEnableSelectedButtons for improved flexibility. The new implementation allows enabling multiple buttons based on a given list and maintains clarity in handling button states. This enhances functionality and aligns with better code practices. * don't think we need to * update --- .../offroad/settings/lateral/mads_settings.cc | 62 ++++++++++++++----- .../offroad/settings/lateral/mads_settings.h | 43 +++++++++++-- .../qt/offroad/settings/lateral_panel.cc | 15 +++++ .../qt/offroad/settings/lateral_panel.h | 11 ++++ sunnypilot/mads/helpers.py | 19 +++--- sunnypilot/mads/mads.py | 3 +- 6 files changed, 126 insertions(+), 27 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc index 97b78399d3..2469bfa593 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.cc @@ -21,22 +21,11 @@ MadsSettings::MadsSettings(QWidget *parent) : QWidget(parent) { ListWidget *list = new ListWidget(this, false); // Main cruise - madsMainCruiseToggle = new ParamControl( - "MadsMainCruiseAllowed", - tr("Toggle with Main Cruise"), - tr("Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement."), - ""); + madsMainCruiseToggle = new ParamControl("MadsMainCruiseAllowed", tr("Toggle with Main Cruise"), "", ""); list->addItem(madsMainCruiseToggle); // Unified Engagement Mode - madsUnifiedEngagementModeToggle = new ParamControl( - "MadsUnifiedEngagementMode", - tr("Unified Engagement Mode (UEM)"), - QString("%1
" - "

%2

") - .arg(tr("Engage lateral and longitudinal control with cruise control engagement.")) - .arg(tr("Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off.")), - ""); + madsUnifiedEngagementModeToggle = new ParamControl("MadsUnifiedEngagementMode", tr("Unified Engagement Mode (UEM)"), "", ""); list->addItem(madsUnifiedEngagementModeToggle); // Steering Mode On Brake @@ -62,8 +51,51 @@ void MadsSettings::updateToggles(bool _offroad) { std::clamp(mads_steering_mode_param, static_cast(MadsSteeringMode::REMAIN_ACTIVE), static_cast(MadsSteeringMode::DISENGAGE)) ); - madsSteeringMode->setEnabled(_offroad); - madsSteeringMode->setDescription(madsSteeringModeDescription(steering_mode)); + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (isBrandInList(CP.getBrand(), mads_limited_settings_brands)) { + params.remove("MadsMainCruiseAllowed"); + params.putBool("MadsUnifiedEngagementMode", true); + params.put("MadsSteeringMode", std::to_string(static_cast(MadsSteeringMode::DISENGAGE))); + + madsMainCruiseToggle->setEnabled(false); + madsMainCruiseToggle->setDescription(madsDescriptionBuilder(DEFAULT_TO_OFF, MADS_MAIN_CRUISE_BASE_DESC)); + madsMainCruiseToggle->showDescription(); + + madsUnifiedEngagementModeToggle->setEnabled(false); + madsUnifiedEngagementModeToggle->setDescription(madsDescriptionBuilder(DEFAULT_TO_ON, MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC)); + madsUnifiedEngagementModeToggle->showDescription(); + + madsSteeringModeValues = convertMadsSteeringModeValues({MadsSteeringMode::DISENGAGE}); + madsSteeringMode->setDescription(madsDescriptionBuilder(STATUS_DISENGAGE_ONLY, madsSteeringModeDescription(steering_mode))); + } else { + madsMainCruiseToggle->setEnabled(true); + madsMainCruiseToggle->setDescription(MADS_MAIN_CRUISE_BASE_DESC); + + madsUnifiedEngagementModeToggle->setEnabled(true); + madsUnifiedEngagementModeToggle->setDescription(MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC); + + madsSteeringModeValues = convertMadsSteeringModeValues(getMadsSteeringModeValues()); + madsSteeringMode->setDescription(madsSteeringModeDescription(steering_mode)); + } + } else { + madsMainCruiseToggle->setEnabled(false); + madsMainCruiseToggle->setDescription(madsDescriptionBuilder(STATUS_CHECK_COMPATIBILITY, MADS_MAIN_CRUISE_BASE_DESC)); + madsMainCruiseToggle->showDescription(); + + madsUnifiedEngagementModeToggle->setEnabled(false); + madsUnifiedEngagementModeToggle->setDescription(madsDescriptionBuilder(STATUS_CHECK_COMPATIBILITY, MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC)); + madsUnifiedEngagementModeToggle->showDescription(); + + madsSteeringModeValues = {}; + madsSteeringMode->setDescription(madsDescriptionBuilder(STATUS_CHECK_COMPATIBILITY, madsSteeringModeDescription(steering_mode))); + } + + madsSteeringMode->setEnableSelectedButtons(_offroad, madsSteeringModeValues); madsSteeringMode->showDescription(); offroad = _offroad; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h index 07ca07834d..405a2613d1 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h @@ -12,6 +12,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +const std::vector mads_limited_settings_brands = {"rivian"}; + enum class MadsSteeringMode { REMAIN_ACTIVE = 0, PAUSE = 1, @@ -46,15 +48,44 @@ private: ParamControl *madsUnifiedEngagementModeToggle; ButtonParamControl *madsSteeringMode; + std::vector madsSteeringModeValues = {}; + + const QString MADS_MAIN_CRUISE_BASE_DESC = tr("Note: For vehicles without LFA/LKAS button, disabling this will prevent lateral control engagement."); + const QString MADS_UNIFIED_ENGAGEMENT_MODE_BASE_DESC = QString("%1
" + "

%2

") + .arg(tr("Engage lateral and longitudinal control with cruise control engagement.")) + .arg(tr("Note: Once lateral control is engaged via UEM, it will remain engaged until it is manually disabled via the MADS button or car shut off.")); + + const QString STATUS_CHECK_COMPATIBILITY = tr("Start the vehicle to check vehicle compatibility."); + const QString DEFAULT_TO_OFF = tr("This feature defaults to OFF, and does not allow selection due to vehicle limitations."); + const QString DEFAULT_TO_ON = tr("This feature defaults to ON, and does not allow selection due to vehicle limitations."); + const QString STATUS_DISENGAGE_ONLY = tr("This platform only supports Disengage mode due to vehicle limitations."); + static const std::vector &madsSteeringModeOptions() { static const std::vector options = { - {MadsSteeringMode::REMAIN_ACTIVE, tr("Remain Active"), tr("Remain Active: ALC will remain active when the brake pedal is pressed.")}, - {MadsSteeringMode::PAUSE, tr("Pause"), tr("Pause: ALC will pause when the brake pedal is pressed.")}, - {MadsSteeringMode::DISENGAGE, tr("Disengage"), tr("Disengage: ALC will disengage when the brake pedal is pressed.")}, + {MadsSteeringMode::REMAIN_ACTIVE, tr("Remain Active"), tr("Remain Active: ALC will remain active when the brake pedal is pressed.")}, + {MadsSteeringMode::PAUSE, tr("Pause"), tr("Pause: ALC will pause when the brake pedal is pressed.")}, + {MadsSteeringMode::DISENGAGE, tr("Disengage"), tr("Disengage: ALC will disengage when the brake pedal is pressed.")}, }; return options; } + static std::vector getMadsSteeringModeValues() { + std::vector values; + for (const auto& option : madsSteeringModeOptions()) { + values.push_back(option.mode); + } + return values; + } + + static std::vector convertMadsSteeringModeValues(const std::vector &modes) { + std::vector values; + for (const auto& mode : modes) { + values.push_back(static_cast(mode)); + } + return values; + } + static std::vector madsSteeringModeTexts() { std::vector texts; for (const auto& option : madsSteeringModeOptions()) { @@ -63,7 +94,7 @@ private: return texts; } - static QString madsSteeringModeDescription(const MadsSteeringMode mode) { + static QString madsSteeringModeDescription(const MadsSteeringMode mode = MadsSteeringMode::REMAIN_ACTIVE) { QString base_desc = tr("Choose how Automatic Lane Centering (ALC) behaves after the brake pedal is manually pressed in sunnypilot."); QString result = base_desc + "

"; @@ -77,4 +108,8 @@ private: return result; } + + static QString madsDescriptionBuilder(const QString &custom_description, const QString &base_description) { + return "" + custom_description + "

" + base_description; + } }; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc index 75cd13664d..0beae6e24f 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc @@ -122,6 +122,21 @@ void LateralPanel::updateToggles(bool _offroad) { toggle->setEnabled(_offroad); } + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + if (isBrandInList(CP.getBrand(), mads_limited_settings_brands)) { + madsToggle->setDescription(descriptionBuilder(STATUS_MADS_SETTINGS_LIMITED_COMPATIBILITY, MADS_BASE_DESC)); + } else { + madsToggle->setDescription(descriptionBuilder(STATUS_MADS_SETTINGS_FULL_COMPATIBILITY, MADS_BASE_DESC)); + } + } else { + madsToggle->setDescription(descriptionBuilder(STATUS_MADS_CHECK_COMPATIBILITY, MADS_BASE_DESC)); + } + madsSettingsButton->setEnabled(madsToggle->isToggled()); offroad = _offroad; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h index 1b77939ee8..cf003dfaf3 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h @@ -30,6 +30,7 @@ public slots: void updateToggles(bool _offroad); private: + Params params; QStackedLayout* main_layout = nullptr; QWidget* sunnypilotScreen = nullptr; ScrollViewSP *sunnypilotScroller = nullptr; @@ -42,4 +43,14 @@ private: PushButtonSP *laneChangeSettingsButton; LaneChangeSettings *laneChangeWidget = nullptr; NeuralNetworkLateralControl *nnlcToggle = nullptr; + + const QString MADS_BASE_DESC = tr("Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC)."); + + const QString STATUS_MADS_CHECK_COMPATIBILITY = tr("Start the vehicle to check vehicle compatibility."); + const QString STATUS_MADS_SETTINGS_FULL_COMPATIBILITY = tr("This platform supports all MADS settings."); + const QString STATUS_MADS_SETTINGS_LIMITED_COMPATIBILITY = tr("This platform supports limited MADS settings."); + + static QString descriptionBuilder(const QString &custom_description, const QString &base_description) { + return "" + custom_description + "

" + base_description; + } }; diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py index 5f7f9840e2..5c7b575d14 100644 --- a/sunnypilot/mads/helpers.py +++ b/sunnypilot/mads/helpers.py @@ -46,12 +46,17 @@ def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, p CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value CP_SP.safetyParam |= HyundaiSafetyFlagsSP.LONG_MAIN_CRUISE_TOGGLEABLE - # MADS is currently not supported in Tesla due to lack of consistent states to engage controls - # TODO-SP: To enable MADS for Tesla, identify consistent signals for MADS toggling - if CP.brand == "tesla": - params.remove("Mads") + # MADS Partial Support + # MADS is currently partially supported for these platforms due to lack of consistent states to engage controls + # Only MadsSteeringModeOnBrake.DISENGAGE is supported for these platforms + mads_partial_support = False + if mads_partial_support: + params.put("MadsSteeringMode", "2") + params.put_bool("MadsUnifiedEngagementMode", True) + params.remove("MadsMainCruiseAllowed") - # MADS is currently not supported in Rivian due to lack of consistent states to engage controls - # TODO-SP: To enable MADS for Rivian, identify consistent signals for MADS toggling - if CP.brand == "rivian": + # MADS Disabled - No Support + # MADS is currently not supported for these platforms due to lack of consistent states to engage controls + # TODO-SP: To enable MADS full support for Rivian/Tesla, identify consistent signals for MADS toggling + if CP.brand in ("rivian", "tesla"): params.remove("Mads") diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index 38d38230ab..ffe6e6efeb 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -33,6 +33,7 @@ class ModularAssistiveDrivingSystem: self.active = False self.available = False self.allow_always = False + self.no_main_cruise = False self.selfdrive = selfdrive self.selfdrive.enabled_prev = False self.state_machine = StateMachine(self) @@ -159,7 +160,7 @@ class ModularAssistiveDrivingSystem: else: self.events_sp.add(EventNameSP.lkasEnable) - if not CS.cruiseState.available: + if not CS.cruiseState.available and not self.no_main_cruise: self.events.remove(EventName.buttonEnable) if self.selfdrive.CS_prev.cruiseState.available: self.events_sp.add(EventNameSP.lkasDisable) From 8201f3edf450f9871e46f2c9db7b4d8483aac767 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 19 May 2025 16:55:43 -0400 Subject: [PATCH 285/477] MADS: Rivian support (#936) * MADS: Tesla and Rivian support * request lateral if acc is engaged * try this out * fix * make sure we keep the toggle off for tesla * namedtuple * disallow steering past 90 degrees for rivian * whoops * Revert "Revert "MADS: Steering Mode on Brake Pedal Press (#687)" (#789)" This reverts commit 8dec4ea5 * both rivian and tesla * enforce disengage on brake steering mode for Rivian and Tesla * wrong one * MADS: Steering Mode on Brake Pedal Press * bump * bump * descriptions * bump * bump * no tesla or rivian yet * codecov v5 * Revert "codecov v5" This reverts commit a347e3fb27c0fbf2510b69fe8148b254fa93b8de. * cleanup * refactor description * sync name * fix * make sure we don't allow if brake was already being pressed * no longer needed * proper ui! * allow LKAS tx at all times with MADS * extra * this ain't right * try this * test only * bring them back * some dynamic checks * dynamic description for mads toggle * one place for limited platforms * just rivian * not here * Revert "not here" This reverts commit 53271b942839feb706e393254a3516097cc72890. * get them out * get them out * no longer needed * Revert "get them out" This reverts commit 532b671bfb8bf76d5b4ef6349e5a1e3cd760dc3e. * bump * bump * less * Revert "bump" This reverts commit 05ee4be04f190aaaa61bf18b0884b09accd74522. --- opendbc_repo | 2 +- sunnypilot/mads/helpers.py | 18 +++++++++++++----- sunnypilot/mads/mads.py | 7 +++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index a051754777..748a84787a 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit a051754777785cc93eb0be35b081d19f7fbce595 +Subproject commit 748a84787ad8b55125cf3953fd08de7f90da10c6 diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py index 5c7b575d14..806f8edbea 100644 --- a/sunnypilot/mads/helpers.py +++ b/sunnypilot/mads/helpers.py @@ -17,7 +17,14 @@ class MadsSteeringModeOnBrake: DISENGAGE = 2 -def read_steering_mode_param(params: Params): +def get_mads_limited_brands(CP: structs.CarParams) -> bool: + return CP.brand in ("rivian", ) + + +def read_steering_mode_param(CP: structs.CarParams, params: Params): + if get_mads_limited_brands(CP): + return MadsSteeringModeOnBrake.DISENGAGE + try: return int(params.get("MadsSteeringMode")) except (ValueError, TypeError): @@ -26,7 +33,7 @@ def read_steering_mode_param(params: Params): def set_alternative_experience(CP: structs.CarParams, params: Params): enabled = params.get_bool("Mads") - steering_mode = read_steering_mode_param(params) + steering_mode = read_steering_mode_param(CP, params) if enabled: CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS @@ -49,7 +56,8 @@ def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, p # MADS Partial Support # MADS is currently partially supported for these platforms due to lack of consistent states to engage controls # Only MadsSteeringModeOnBrake.DISENGAGE is supported for these platforms - mads_partial_support = False + # TODO-SP: To enable MADS full support for Rivian, identify consistent signals for MADS toggling + mads_partial_support = get_mads_limited_brands(CP) if mads_partial_support: params.put("MadsSteeringMode", "2") params.put_bool("MadsUnifiedEngagementMode", True) @@ -57,6 +65,6 @@ def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, p # MADS Disabled - No Support # MADS is currently not supported for these platforms due to lack of consistent states to engage controls - # TODO-SP: To enable MADS full support for Rivian/Tesla, identify consistent signals for MADS toggling - if CP.brand in ("rivian", "tesla"): + # TODO-SP: To enable MADS full support for Tesla, identify consistent signals for MADS toggling + if CP.brand in ("tesla", ): params.remove("Mads") diff --git a/sunnypilot/mads/mads.py b/sunnypilot/mads/mads.py index ffe6e6efeb..3b06290569 100644 --- a/sunnypilot/mads/mads.py +++ b/sunnypilot/mads/mads.py @@ -10,7 +10,7 @@ from cereal import log, custom from opendbc.car import structs from opendbc.car.hyundai.values import HyundaiFlags from opendbc.safety import ALTERNATIVE_EXPERIENCE -from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param +from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param, get_mads_limited_brands from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState @@ -45,10 +45,13 @@ class ModularAssistiveDrivingSystem: if self.CP.flags & (HyundaiFlags.HAS_LDA_BUTTON | HyundaiFlags.CANFD): self.allow_always = True + if get_mads_limited_brands(self.CP): + self.no_main_cruise = True + # read params on init self.enabled_toggle = self.params.get_bool("Mads") self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed") - self.steering_mode_on_brake = read_steering_mode_param(self.params) + self.steering_mode_on_brake = read_steering_mode_param(self.CP, self.params) self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode") def read_params(self): From 8752071049e94a8fc8ff5d086f24071b1210f788 Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Mon, 19 May 2025 16:16:30 -0700 Subject: [PATCH 286/477] [bot] Update Python packages (#35282) Update Python packages Co-authored-by: Vehicle Researcher --- panda | 2 +- tinygrad_repo | 2 +- uv.lock | 88 +++++++++++++++++++++++++-------------------------- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/panda b/panda index 8bd83750c2..471a455789 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 8bd83750c2c0a5b8f68a00717f7516ae612fb5ab +Subproject commit 471a455789b88a0bf1098e87c49d564da266e890 diff --git a/tinygrad_repo b/tinygrad_repo index 6f77b938d7..f9a5ad24c5 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 6f77b938d77395a34f02c7d7d904d80fcd81b9cd +Subproject commit f9a5ad24c534c0fe3cb2ab8a307b2cc71efc13ea diff --git a/uv.lock b/uv.lock index cd9c84af69..0c4169e0bf 100644 --- a/uv.lock +++ b/uv.lock @@ -447,31 +447,31 @@ wheels = [ [[package]] name = "cython" -version = "3.1.0" +version = "3.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/f7/db37a613aec5abcd51c8000a386a701ac32e94659aa03fa69c3e5c19b149/cython-3.1.0.tar.gz", hash = "sha256:1097dd60d43ad0fff614a57524bfd531b35c13a907d13bee2cc2ec152e6bf4a1", size = 3181017, upload-time = "2025-05-08T20:25:36.12Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/d3/bb000603e46144db2e5055219bbddcf7ab3b10012fcb342695694fb88141/cython-3.1.1.tar.gz", hash = "sha256:505ccd413669d5132a53834d792c707974248088c4f60c497deb1b416e366397", size = 3175446, upload-time = "2025-05-19T09:44:54.347Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/bd/eb20821704e0bac5051ae552db574cf5022ba658006a0c187d85d50cfd69/cython-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c736405078dc376502617eb41c39e223ae176ebd1a4ddc18179d2517bc8c8658", size = 3004811, upload-time = "2025-05-08T21:20:38.179Z" }, - { url = "https://files.pythonhosted.org/packages/59/6f/2f4e1382e365006055ec7b803372283065c4cef6f5bab88f8ed2ab350452/cython-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1215d3adb4e8691d03e712aed31206d21f387a8003d8de6a574ee75fe6d2e07c", size = 2868305, upload-time = "2025-05-08T21:20:41.1Z" }, - { url = "https://files.pythonhosted.org/packages/6e/b3/9778e21b317c10e9a1028270eb87eda91202cd37ee1e0c09630a600629ef/cython-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:522d4dae1fea71eee5c944fb7a8530de8bdd6df0ccb2bd001d0f75be228eac6c", size = 3130151, upload-time = "2025-05-08T21:20:44.436Z" }, - { url = "https://files.pythonhosted.org/packages/58/e7/4fe8304d1caccff52a4c042274b42bf23574a380e64a86daaf4c695be510/cython-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462ad6142057e81715ada74e2d24b9a07bf36ae3da72bf973478b5c3e809c26d", size = 3220382, upload-time = "2025-05-08T21:20:47.232Z" }, - { url = "https://files.pythonhosted.org/packages/0a/88/9d3d7a1cb8ce3e93acbabac6f2275cfaa1021c2ddf93d2c0999bb2ee4d43/cython-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8f00cdeb14f004ebeacf946e06bad2e3ed5776af96f5af95f92d822c4ba275f", size = 3287586, upload-time = "2025-05-08T21:20:49.72Z" }, - { url = "https://files.pythonhosted.org/packages/91/49/99148b55ad1c036fb1cb69bf3729d2beecc4246f7c5f487006f16ec1f394/cython-3.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37d62b8b8919126c75769e5470b288d76c83a1645e73c7aca4b7d7aecb3c1234", size = 3175407, upload-time = "2025-05-08T21:20:51.819Z" }, - { url = "https://files.pythonhosted.org/packages/0e/52/297ce50c21647ae3741856e57f04ba69b1819e5658756823a3a1efd05e8e/cython-3.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bea0b6bfde7493acb0529fc603abd4b3b13c3bb2fff7a889ae5a8d3ea7dc5a84", size = 3381904, upload-time = "2025-05-08T21:20:53.941Z" }, - { url = "https://files.pythonhosted.org/packages/61/ab/2966d2786f4e6a7903d23b019a810675334960e8d88686a985286bd8f53d/cython-3.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fe8c1db9ec03d9ef83e33c842c108e892577ade4c5f530c9435beced048e4698", size = 3305998, upload-time = "2025-05-08T21:20:56.588Z" }, - { url = "https://files.pythonhosted.org/packages/62/52/0670d10b563b2f345477a158bd133399ddb9e7f6ff16e397e77932c07026/cython-3.1.0-cp311-cp311-win32.whl", hash = "sha256:5f6417d378bd11ca55f16e3c1c7c3bf6d7f0a0cc326c46e309fcba46c54ba4f1", size = 2454005, upload-time = "2025-05-08T21:20:58.698Z" }, - { url = "https://files.pythonhosted.org/packages/53/6e/3ea341450bc40f3c0c3da9ae05c22eeb59930531accba78a4b3eebaf342c/cython-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:dde3726aa5acbe879f849a09606b886491f950cfa993b435e50e9561fdf731c6", size = 2663258, upload-time = "2025-05-08T21:21:00.903Z" }, - { url = "https://files.pythonhosted.org/packages/e9/64/ae1d8848550ec3975634fcf189ccc85e73c3b9f76369dd85c484f2f8f1c3/cython-3.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8f8c4753f6b926046c0cdf6037ba8560f6677730bf0ab9c1db4e0163b4bb30f9", size = 3012737, upload-time = "2025-05-08T21:21:06.024Z" }, - { url = "https://files.pythonhosted.org/packages/cc/e6/c8fe5e71b4272a1b5683a006bfe4bfeb5e2691c6603b44d18eb57772f626/cython-3.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db8e15c8eeee529468eab08528c9bf714a94354b34375be6c0c110f6012a4768", size = 2843774, upload-time = "2025-05-08T21:21:08.112Z" }, - { url = "https://files.pythonhosted.org/packages/6a/eb/aeb62d32049ab55555e679b7d0cdb2f8c16f6a9d1189a56a96a0c1e86083/cython-3.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a46b34defa672268474fbb5541f6297f45df9e4ecc4def6edd6fe1c44bfdb795", size = 3080908, upload-time = "2025-05-08T21:21:10.228Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7a/5d5507f1fafd3215ea7d01e1c78809ca71dddd88b4ce75dba3ca53ab2b3b/cython-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8818446612461aca3978ebe8e3def817a120d91f85022540843ebe4f24818cd6", size = 3197585, upload-time = "2025-05-08T21:21:12.354Z" }, - { url = "https://files.pythonhosted.org/packages/f8/97/34dd0112d042ec85736273acb2caced7e576681fbf003bcefd007bbd36d2/cython-3.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe401e825b0fbeec75f8cc758c8cf32345c673bdb0edaf9585cd43b9d2798824", size = 3244153, upload-time = "2025-05-08T21:21:14.692Z" }, - { url = "https://files.pythonhosted.org/packages/fb/98/77c50a153f4acdf7b48d055c5387edbcd3de1e0d17affca5181cb009f702/cython-3.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c96908b302e87e99915b3b66481a976e32b864e95bf054dcd2cb859dffd8cb10", size = 3123440, upload-time = "2025-05-08T21:21:16.873Z" }, - { url = "https://files.pythonhosted.org/packages/63/22/ecb9301ee1efbb0b3a6be371fd01be87d45113ed2fe804897d399da8af6f/cython-3.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cdde5f25fdb8a5d50dbe5d418fe5bfb2260b1acdbd45b788e77b247e9adf2f56", size = 3337569, upload-time = "2025-05-08T21:21:19.799Z" }, - { url = "https://files.pythonhosted.org/packages/fa/67/b707c2548cc2f790c60bb634b996f93e748ad081681c28fe7a22591e663e/cython-3.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe3320d13cde70fa8b1936e633b9e0fa68720cc61f97aa371d56d0f84fba3e02", size = 3289049, upload-time = "2025-05-08T21:21:21.997Z" }, - { url = "https://files.pythonhosted.org/packages/be/61/05b58fc96248b2d6e487f1f8d20217d3ce6d4ba5b621fb315b23c87d077c/cython-3.1.0-cp312-cp312-win32.whl", hash = "sha256:d41d17d7cfcfbddf3b7dc0ceddb6361b8e749b0b3c5f8efa40c31c249127fa15", size = 2469164, upload-time = "2025-05-08T21:21:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/4a/a1/74009b116dbec7d6c721ac268a4aa2c0c0069c99c138586f27c8fb77cae3/cython-3.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:61eb67401bd6c977084fc789812bd40f96be500049adb2bab99921d696ae0c87", size = 2673707, upload-time = "2025-05-08T21:21:27.716Z" }, - { url = "https://files.pythonhosted.org/packages/9f/cc/221af506254978b119a2f6769b81b16bcfe09e0fb3fc5ab66e53e536d933/cython-3.1.0-py3-none-any.whl", hash = "sha256:4e460bdf1d8742ddf4914959842f2f23ca4934df97f864be799ddf1912acd0ab", size = 1227398, upload-time = "2025-05-08T20:25:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/35/b3/bc75c0352214b5ced31ce5e0d051d0ad4ad916aa7a1d669d1876ad1e59aa/cython-3.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c360823e1063784efc2335617e0f28573d7a594c5a8a05d85e850a9621cccb1f", size = 2998590, upload-time = "2025-05-19T09:56:51.148Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0a/5840cdd7a1e8c0d2ffeb5e09afd32b8d10321cce33a2554ef10ea832a200/cython-3.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:12e00b88147b03c148a95365f89dc1c45a0fc52f9c35aa75ff770ef65b615839", size = 2860818, upload-time = "2025-05-19T09:56:53.694Z" }, + { url = "https://files.pythonhosted.org/packages/63/2e/0fac02ce46f208af54d76c6c786b8dddeb207ca94aa85b0455f6cbaa472c/cython-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab644415458d782c16ba7252de9cec1e3125371641cafea2e53a8c1cf85dd58d", size = 3124262, upload-time = "2025-05-19T09:56:56.277Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/b7ae247b83b3f98966184c1a787001964132ae2e05a989769b1a5e7325da/cython-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5cb6c054daadaf01a88c8f49f3edd9e829c9b76a82cbb4269e3f9878254540b", size = 3215216, upload-time = "2025-05-19T09:56:58.674Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3b/c4e6c16b099a592dbd6cd615c4de901eb8cc2795d5445d77b8cd378de7da/cython-3.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af8f62cc9339b75fe8434325083e6a7cae88c9c21efd74bbb6ba4e3623219469", size = 3282597, upload-time = "2025-05-19T09:57:00.85Z" }, + { url = "https://files.pythonhosted.org/packages/06/dd/90a8fd8508298f1f16e2cbc665774047fbc81f0370125b6e32f0a182fc10/cython-3.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:689c1aad373556bd2ab1aa1c2dad8939a2891465a1fbd2cbbdd42b488fb40ec8", size = 3175592, upload-time = "2025-05-19T09:57:03.1Z" }, + { url = "https://files.pythonhosted.org/packages/af/ab/2cd4e8d5c46499ea2ca5b7c3ae4053db86465b35a8870ce5e847fee06aff/cython-3.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:953046c190fa9ab9a09a546a909b847cdbb4c1fe34e9bfa4a15b6ee1585a86aa", size = 3378435, upload-time = "2025-05-19T09:57:05.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/99/250fb399af2edd9861774f0c8b6c4b7d862aa52d966a07b860bcd723842a/cython-3.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:755a991601b27dd3555310d0f95b19a05e622a80d7b4e7a91fa6f5f3ef3f3b80", size = 3300519, upload-time = "2025-05-19T09:57:07.512Z" }, + { url = "https://files.pythonhosted.org/packages/37/09/1c5d470580d9b92107cadedc848f43e2f2102284f8b666ea9ab82f6fc101/cython-3.1.1-cp311-cp311-win32.whl", hash = "sha256:83b2af5c327f7da4f08afc34fddfaf6d24fa0c000b6b70a527c8125e493b6080", size = 2447287, upload-time = "2025-05-19T09:57:09.958Z" }, + { url = "https://files.pythonhosted.org/packages/30/67/c99ec81380cd9d2c798eb1572f61dbe50318958925049b39029f73fe6b52/cython-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:141ffd6279411c562f6b707adc56b63e965a4fd7f21db83f5d4fcbd8c50ac546", size = 2655739, upload-time = "2025-05-19T09:57:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/83ff82381319ff68ae46f9dd3024b1d5101997e81a8e955811525b6f934b/cython-3.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9d7dc0e4d0cd491fac679a61e9ede348c64ca449f99a284f9a01851aa1dbc7f6", size = 3006334, upload-time = "2025-05-19T09:57:14.284Z" }, + { url = "https://files.pythonhosted.org/packages/c3/01/b4c46c6a27cd2da642bc987c1f9087265defbc96a1929d326b9034953f15/cython-3.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd689910002adfac8734f237cdea1573e38345f27ed7fd445482813b65a29457", size = 2836861, upload-time = "2025-05-19T09:57:16.129Z" }, + { url = "https://files.pythonhosted.org/packages/96/51/7936c5d01ec3c89be8de1756f284878d4a567627b7b1790455ac627fb833/cython-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10f0434916994fe213ea7749268b88d77e3ebcbd1b99542cf64bb7d180f45470", size = 3074560, upload-time = "2025-05-19T09:57:18.797Z" }, + { url = "https://files.pythonhosted.org/packages/0d/81/34aeb787dcb2624a82a33e60276ed28d2da8a08c79660cf674b19be82248/cython-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:873aac4ac0b0fb197557c0ac15458b780b9221daa4a716881cbd1a9016c8459f", size = 3192645, upload-time = "2025-05-19T09:57:21.002Z" }, + { url = "https://files.pythonhosted.org/packages/e8/bf/1350ed6cb48158a4f096306a12bc4c26c6d20d3314f1f1978ea23afe0220/cython-3.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23b886a6c8a50b1101ccef2f2f3dc9c699b77633ef5bb5007090226c2ad3f9c2", size = 3241751, upload-time = "2025-05-19T09:57:23.118Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f5/9ed5a898c41723e3da2317fd1f082d963ff08571caeded31cb945be589b5/cython-3.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:dff0e7dd53a0ca35b64cda843253d5cac944db26663dc097b3a1adf2c49514ad", size = 3123562, upload-time = "2025-05-19T09:57:25.492Z" }, + { url = "https://files.pythonhosted.org/packages/c3/81/b5ce4393d3a0a75a8c6d9ad0b80a62263d892260b816eb3d569ef144511a/cython-3.1.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f7954b0b4b3302655d3caa6924261de5907a4e129bc22ace52fe9ae0cd5a758", size = 3333555, upload-time = "2025-05-19T09:57:29.232Z" }, + { url = "https://files.pythonhosted.org/packages/db/47/2c1fa4b4901f10d00e666931dd68d4bd7954d3caa900544d135424ef6178/cython-3.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfa500fd7ae95ca152a5f8062b870532fa3e27efcef6d00612e1f28b9f72615f", size = 3282112, upload-time = "2025-05-19T09:57:31.904Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5b/b000d3ebff79429419d846e06c5c09f33fd010e1f6158d7ba553e1082253/cython-3.1.1-cp312-cp312-win32.whl", hash = "sha256:cd748fab8e4426dbcb2e0fa2979558333934d24365e0de5672fbabfe337d880c", size = 2462293, upload-time = "2025-05-19T09:57:34.964Z" }, + { url = "https://files.pythonhosted.org/packages/45/0e/e1370ed3216e4e164232d1891c2a2932a3874d1a8681f8c3565cafd98579/cython-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:307f216ed319ea07644f2ef9974406c830f01bc8e677e2147e9bfcdf9e3ca8ad", size = 2666710, upload-time = "2025-05-19T09:57:37.528Z" }, + { url = "https://files.pythonhosted.org/packages/a7/97/8e8637e67afc09f1b51a617b15a0d1caf0b5159b0f79d47ab101e620e491/cython-3.1.1-py3-none-any.whl", hash = "sha256:07621e044f332d18139df2ccfcc930151fd323c2f61a58c82f304cffc9eb5280", size = 1220898, upload-time = "2025-05-19T09:44:50.614Z" }, ] [[package]] @@ -4779,15 +4779,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.28.0" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/bb/6a41b2e0e9121bed4d2ec68d50568ab95c49f4744156a9bbb789c866c66d/sentry_sdk-2.28.0.tar.gz", hash = "sha256:14d2b73bc93afaf2a9412490329099e6217761cbab13b6ee8bc0e82927e1504e", size = 325052, upload-time = "2025-05-12T07:53:12.785Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/ec/9a313e221bb24c162f176cd7e04634f5980ce00a431befc2b2e0f78669d2/sentry_sdk-2.29.0.tar.gz", hash = "sha256:a7c6c77c46c12492c41dd70b29d481daa67cc173618c8fa60792737dbefc516b", size = 325543, upload-time = "2025-05-19T13:46:07.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/4e/b1575833094c088dfdef63fbca794518860fcbc8002aadf51ebe8b6a387f/sentry_sdk-2.28.0-py2.py3-none-any.whl", hash = "sha256:51496e6cb3cb625b99c8e08907c67a9112360259b0ef08470e532c3ab184a232", size = 341693, upload-time = "2025-05-12T07:53:10.882Z" }, + { url = "https://files.pythonhosted.org/packages/9d/cf/fa451f0518f76b21f209a71684738a3c9aa0e36ead786a5f806c3092e647/sentry_sdk-2.29.0-py2.py3-none-any.whl", hash = "sha256:5234404e2208acbce051e374f0cfc4727d69b33c5aafa5109056949b5b847c92", size = 341552, upload-time = "2025-05-19T13:46:05.067Z" }, ] [[package]] @@ -4833,29 +4833,29 @@ wheels = [ [[package]] name = "shapely" -version = "2.1.0" +version = "2.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/fe/3b0d2f828ffaceadcdcb51b75b9c62d98e62dd95ce575278de35f24a1c20/shapely-2.1.0.tar.gz", hash = "sha256:2cbe90e86fa8fc3ca8af6ffb00a77b246b918c7cf28677b7c21489b678f6b02e", size = 313617, upload-time = "2025-04-03T09:15:05.725Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/3c/2da625233f4e605155926566c0e7ea8dda361877f48e8b1655e53456f252/shapely-2.1.1.tar.gz", hash = "sha256:500621967f2ffe9642454808009044c21e5b35db89ce69f8a2042c2ffd0e2772", size = 315422, upload-time = "2025-05-19T11:04:41.265Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/37/ae448f06f363ff3dfe4bae890abd842c4e3e9edaf01245dbc9b97008c9e6/shapely-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8323031ef7c1bdda7a92d5ddbc7b6b62702e73ba37e9a8ccc8da99ec2c0b87c", size = 1820974, upload-time = "2025-04-03T09:14:11.301Z" }, - { url = "https://files.pythonhosted.org/packages/78/da/ea2a898e93c6953c5eef353a0e1781a0013a1352f2b90aa9ab0b800e0c75/shapely-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4da7c6cd748d86ec6aace99ad17129d30954ccf5e73e9911cdb5f0fa9658b4f8", size = 1624137, upload-time = "2025-04-03T09:14:13.127Z" }, - { url = "https://files.pythonhosted.org/packages/64/4a/f903f82f0fabcd3f43ea2e8132cabda079119247330a9fe58018c39c4e22/shapely-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f0cdf85ff80831137067e7a237085a3ee72c225dba1b30beef87f7d396cf02b", size = 2957161, upload-time = "2025-04-03T09:14:15.031Z" }, - { url = "https://files.pythonhosted.org/packages/92/07/3e2738c542d73182066196b8ce99388cb537d19e300e428d50b1537e3b21/shapely-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f2be5d79aac39886f23000727cf02001aef3af8810176c29ee12cdc3ef3a50", size = 3078530, upload-time = "2025-04-03T09:14:16.562Z" }, - { url = "https://files.pythonhosted.org/packages/82/08/32210e63d8f8af9142d37c2433ece4846862cdac91a0fe66f040780a71bd/shapely-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:21a4515009f56d7a159cf5c2554264e82f56405b4721f9a422cb397237c5dca8", size = 3902208, upload-time = "2025-04-03T09:14:18.342Z" }, - { url = "https://files.pythonhosted.org/packages/19/0e/0abb5225f8a32fbdb615476637038a7d2db40c0af46d1bb3a08b869bee39/shapely-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cebc323cec2cb6b2eaa310fdfc621f6dbbfaf6bde336d13838fcea76c885a9", size = 4082863, upload-time = "2025-04-03T09:14:20.233Z" }, - { url = "https://files.pythonhosted.org/packages/f8/1b/7cd816fd388108c872ab7e2930180b02d0c34891213f361e4a66e5e032f2/shapely-2.1.0-cp311-cp311-win32.whl", hash = "sha256:cad51b7a5c8f82f5640472944a74f0f239123dde9a63042b3c5ea311739b7d20", size = 1527488, upload-time = "2025-04-03T09:14:21.597Z" }, - { url = "https://files.pythonhosted.org/packages/fd/28/7bb5b1944d4002d4b2f967762018500381c3b532f98e456bbda40c3ded68/shapely-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4005309dde8658e287ad9c435c81877f6a95a9419b932fa7a1f34b120f270ae", size = 1708311, upload-time = "2025-04-03T09:14:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d1/6a9371ec39d3ef08e13225594e6c55b045209629afd9e6d403204507c2a8/shapely-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53e7ee8bd8609cf12ee6dce01ea5affe676976cf7049315751d53d8db6d2b4b2", size = 1830732, upload-time = "2025-04-03T09:14:25.047Z" }, - { url = "https://files.pythonhosted.org/packages/32/87/799e3e48be7ce848c08509b94d2180f4ddb02e846e3c62d0af33da4d78d3/shapely-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cab20b665d26dbec0b380e15749bea720885a481fa7b1eedc88195d4a98cfa4", size = 1638404, upload-time = "2025-04-03T09:14:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/85/00/6665d77f9dd09478ab0993b8bc31668aec4fd3e5f1ddd1b28dd5830e47be/shapely-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a38b39a09340273c3c92b3b9a374272a12cc7e468aeeea22c1c46217a03e5c", size = 2945316, upload-time = "2025-04-03T09:14:28.266Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/738e07d10bbc67cae0dcfe5a484c6e518a517f4f90550dda2adf3a78b9f2/shapely-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edaec656bdd9b71278b98e6f77c464b1c3b2daa9eace78012ff0f0b4b5b15b04", size = 3063099, upload-time = "2025-04-03T09:14:30.067Z" }, - { url = "https://files.pythonhosted.org/packages/88/b8/138098674559362ab29f152bff3b6630de423378fbb0324812742433a4ef/shapely-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c8a732ddd9b25e7a54aa748e7df8fd704e23e5d5d35b7d376d80bffbfc376d04", size = 3887873, upload-time = "2025-04-03T09:14:31.912Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fdae7c2db009244991d86f4d2ca09d2f5ccc9d41c312c3b1ee1404dc55da/shapely-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9c93693ad8adfdc9138a5a2d42da02da94f728dd2e82d2f0f442f10e25027f5f", size = 4067004, upload-time = "2025-04-03T09:14:33.976Z" }, - { url = "https://files.pythonhosted.org/packages/ed/78/17e17d91b489019379df3ee1afc4bd39787b232aaa1d540f7d376f0280b7/shapely-2.1.0-cp312-cp312-win32.whl", hash = "sha256:d8ac6604eefe807e71a908524de23a37920133a1729fe3a4dfe0ed82c044cbf4", size = 1527366, upload-time = "2025-04-03T09:14:35.348Z" }, - { url = "https://files.pythonhosted.org/packages/b8/bd/9249bd6dda948441e25e4fb14cbbb5205146b0fff12c66b19331f1ff2141/shapely-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:f4f47e631aa4f9ec5576eac546eb3f38802e2f82aeb0552f9612cb9a14ece1db", size = 1708265, upload-time = "2025-04-03T09:14:36.878Z" }, + { url = "https://files.pythonhosted.org/packages/19/97/2df985b1e03f90c503796ad5ecd3d9ed305123b64d4ccb54616b30295b29/shapely-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:587a1aa72bc858fab9b8c20427b5f6027b7cbc92743b8e2c73b9de55aa71c7a7", size = 1819368, upload-time = "2025-05-19T11:03:55.937Z" }, + { url = "https://files.pythonhosted.org/packages/56/17/504518860370f0a28908b18864f43d72f03581e2b6680540ca668f07aa42/shapely-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fa5c53b0791a4b998f9ad84aad456c988600757a96b0a05e14bba10cebaaaea", size = 1625362, upload-time = "2025-05-19T11:03:57.06Z" }, + { url = "https://files.pythonhosted.org/packages/36/a1/9677337d729b79fce1ef3296aac6b8ef4743419086f669e8a8070eff8f40/shapely-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aabecd038841ab5310d23495253f01c2a82a3aedae5ab9ca489be214aa458aa7", size = 2999005, upload-time = "2025-05-19T11:03:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/a2/17/e09357274699c6e012bbb5a8ea14765a4d5860bb658df1931c9f90d53bd3/shapely-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586f6aee1edec04e16227517a866df3e9a2e43c1f635efc32978bb3dc9c63753", size = 3108489, upload-time = "2025-05-19T11:04:00.059Z" }, + { url = "https://files.pythonhosted.org/packages/17/5d/93a6c37c4b4e9955ad40834f42b17260ca74ecf36df2e81bb14d12221b90/shapely-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b9878b9e37ad26c72aada8de0c9cfe418d9e2ff36992a1693b7f65a075b28647", size = 3945727, upload-time = "2025-05-19T11:04:01.786Z" }, + { url = "https://files.pythonhosted.org/packages/a3/1a/ad696648f16fd82dd6bfcca0b3b8fbafa7aacc13431c7fc4c9b49e481681/shapely-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9a531c48f289ba355e37b134e98e28c557ff13965d4653a5228d0f42a09aed0", size = 4109311, upload-time = "2025-05-19T11:04:03.134Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/150dd245beab179ec0d4472bf6799bf18f21b1efbef59ac87de3377dbf1c/shapely-2.1.1-cp311-cp311-win32.whl", hash = "sha256:4866de2673a971820c75c0167b1f1cd8fb76f2d641101c23d3ca021ad0449bab", size = 1522982, upload-time = "2025-05-19T11:04:05.217Z" }, + { url = "https://files.pythonhosted.org/packages/93/5b/842022c00fbb051083c1c85430f3bb55565b7fd2d775f4f398c0ba8052ce/shapely-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:20a9d79958b3d6c70d8a886b250047ea32ff40489d7abb47d01498c704557a93", size = 1703872, upload-time = "2025-05-19T11:04:06.791Z" }, + { url = "https://files.pythonhosted.org/packages/fb/64/9544dc07dfe80a2d489060791300827c941c451e2910f7364b19607ea352/shapely-2.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2827365b58bf98efb60affc94a8e01c56dd1995a80aabe4b701465d86dcbba43", size = 1833021, upload-time = "2025-05-19T11:04:08.022Z" }, + { url = "https://files.pythonhosted.org/packages/07/aa/fb5f545e72e89b6a0f04a0effda144f5be956c9c312c7d4e00dfddbddbcf/shapely-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9c551f7fa7f1e917af2347fe983f21f212863f1d04f08eece01e9c275903fad", size = 1643018, upload-time = "2025-05-19T11:04:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/03/46/61e03edba81de729f09d880ce7ae5c1af873a0814206bbfb4402ab5c3388/shapely-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78dec4d4fbe7b1db8dc36de3031767e7ece5911fb7782bc9e95c5cdec58fb1e9", size = 2986417, upload-time = "2025-05-19T11:04:10.56Z" }, + { url = "https://files.pythonhosted.org/packages/1f/1e/83ec268ab8254a446b4178b45616ab5822d7b9d2b7eb6e27cf0b82f45601/shapely-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:872d3c0a7b8b37da0e23d80496ec5973c4692920b90de9f502b5beb994bbaaef", size = 3098224, upload-time = "2025-05-19T11:04:11.903Z" }, + { url = "https://files.pythonhosted.org/packages/f1/44/0c21e7717c243e067c9ef8fa9126de24239f8345a5bba9280f7bb9935959/shapely-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e2b9125ebfbc28ecf5353511de62f75a8515ae9470521c9a693e4bb9fbe0cf1", size = 3925982, upload-time = "2025-05-19T11:04:13.224Z" }, + { url = "https://files.pythonhosted.org/packages/15/50/d3b4e15fefc103a0eb13d83bad5f65cd6e07a5d8b2ae920e767932a247d1/shapely-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4b96cea171b3d7f6786976a0520f178c42792897653ecca0c5422fb1e6946e6d", size = 4089122, upload-time = "2025-05-19T11:04:14.477Z" }, + { url = "https://files.pythonhosted.org/packages/bd/05/9a68f27fc6110baeedeeebc14fd86e73fa38738c5b741302408fb6355577/shapely-2.1.1-cp312-cp312-win32.whl", hash = "sha256:39dca52201e02996df02e447f729da97cfb6ff41a03cb50f5547f19d02905af8", size = 1522437, upload-time = "2025-05-19T11:04:16.203Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e9/a4560e12b9338842a1f82c9016d2543eaa084fce30a1ca11991143086b57/shapely-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:13d643256f81d55a50013eff6321142781cf777eb6a9e207c2c9e6315ba6044a", size = 1703479, upload-time = "2025-05-19T11:04:18.497Z" }, ] [[package]] From 9d60846b7041fcc79b93ec2707aeab88dff34016 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 19 May 2025 22:17:38 -0400 Subject: [PATCH 287/477] MADS: Tesla support (#682) * MADS: Tesla and Rivian support * request lateral if acc is engaged * try this out * fix * make sure we keep the toggle off for tesla * namedtuple * disallow steering past 90 degrees for rivian * whoops * Revert "Revert "MADS: Steering Mode on Brake Pedal Press (#687)" (#789)" This reverts commit 8dec4ea5 * both rivian and tesla * enforce disengage on brake steering mode for Rivian and Tesla * wrong one * MADS: Steering Mode on Brake Pedal Press * bump * bump * descriptions * bump * bump * no tesla or rivian yet * codecov v5 * Revert "codecov v5" This reverts commit a347e3fb27c0fbf2510b69fe8148b254fa93b8de. * cleanup * refactor description * sync name * fix * make sure we don't allow if brake was already being pressed * no longer needed * proper ui! * allow LKAS tx at all times with MADS * extra * this ain't right * try this * test only * bring them back * some dynamic checks * dynamic description for mads toggle * one place for limited platforms * update tests * angle when long, lkas when mads * bump --- opendbc_repo | 2 +- .../qt/offroad/settings/lateral/mads_settings.h | 2 +- sunnypilot/mads/helpers.py | 10 ++-------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 748a84787a..5db6b93bc5 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 748a84787ad8b55125cf3953fd08de7f90da10c6 +Subproject commit 5db6b93bc550edf5425d0b22e49c9bf51ec23eab diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h index 405a2613d1..af624865c7 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h @@ -12,7 +12,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" -const std::vector mads_limited_settings_brands = {"rivian"}; +const std::vector mads_limited_settings_brands = {"rivian", "tesla"}; enum class MadsSteeringMode { REMAIN_ACTIVE = 0, diff --git a/sunnypilot/mads/helpers.py b/sunnypilot/mads/helpers.py index 806f8edbea..4b1095c63e 100644 --- a/sunnypilot/mads/helpers.py +++ b/sunnypilot/mads/helpers.py @@ -18,7 +18,7 @@ class MadsSteeringModeOnBrake: def get_mads_limited_brands(CP: structs.CarParams) -> bool: - return CP.brand in ("rivian", ) + return CP.brand in ("rivian", "tesla") def read_steering_mode_param(CP: structs.CarParams, params: Params): @@ -56,15 +56,9 @@ def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, p # MADS Partial Support # MADS is currently partially supported for these platforms due to lack of consistent states to engage controls # Only MadsSteeringModeOnBrake.DISENGAGE is supported for these platforms - # TODO-SP: To enable MADS full support for Rivian, identify consistent signals for MADS toggling + # TODO-SP: To enable MADS full support for Rivian/Tesla, identify consistent signals for MADS toggling mads_partial_support = get_mads_limited_brands(CP) if mads_partial_support: params.put("MadsSteeringMode", "2") params.put_bool("MadsUnifiedEngagementMode", True) params.remove("MadsMainCruiseAllowed") - - # MADS Disabled - No Support - # MADS is currently not supported for these platforms due to lack of consistent states to engage controls - # TODO-SP: To enable MADS full support for Tesla, identify consistent signals for MADS toggling - if CP.brand in ("tesla", ): - params.remove("Mads") From f00bead198a9957c978f3622fee0aa45a9bfb483 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 20 May 2025 17:22:34 +0800 Subject: [PATCH 288/477] system/ui: improve button click logic for proper press-release interaction (#35289) improve button click logic for proper press,release interaction --- system/ui/lib/button.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index 69a5da646e..4870e333df 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -41,6 +41,8 @@ BUTTON_PRESSED_BACKGROUND_COLORS = { ButtonStyle.ACTION: rl.Color(130, 130, 130, 255), } +_pressed_buttons: set[str] = set() # Track mouse press state globally + def gui_button( rect: rl.Rectangle, @@ -54,6 +56,7 @@ def gui_button( text_padding: int = 20, # Padding for left/right alignment icon=None, ) -> int: + button_id = f"{rect.x}_{rect.y}_{rect.width}_{rect.height}" result = 0 if button_style in (ButtonStyle.PRIMARY, ButtonStyle.DANGER) and not is_enabled: @@ -64,11 +67,24 @@ def gui_button( # Set background color based on button type bg_color = BUTTON_BACKGROUND_COLORS[button_style] - if is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect): - if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): + mouse_over = is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect) + + if mouse_over: + if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + # Record that this button was pressed + _pressed_buttons.add(button_id) + bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] + elif rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): - result = 1 + # Check if this button was previously pressed + if button_id in _pressed_buttons: + result = 1 + _pressed_buttons.remove(button_id) + + # Clean up pressed state if mouse is released anywhere + if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and button_id in _pressed_buttons: + _pressed_buttons.remove(button_id) # Draw the button with rounded corners roundness = border_radius / (min(rect.width, rect.height) / 2) From f646b4a17ba4cd7e0e396cce250539ea9838eecb Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 20 May 2025 17:46:56 +0800 Subject: [PATCH 289/477] system/ui: remove special currency and symbol characters from keyboard (#35291) * remove special currency and symbol characters from keyboard * bring back --------- Co-authored-by: Cameron Clough --- system/ui/widgets/keyboard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 85608c1ef5..dfe14e7f40 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -43,7 +43,7 @@ KEYBOARD_LAYOUTS = { ], "specials": [ ["[", "]", "{", "}", "#", "%", "^", "*", "+", "="], - ["_", "\\", "|", "~", "<", ">", "€", "£", "¥", "•"], + ["_", "\\", "|", "~", "<", ">"], [NUMERIC_KEY, ".", ",", "?", "!", "'", BACKSPACE_KEY], [ABC_KEY, SPACE_KEY, ".", ENTER_KEY], ], @@ -112,6 +112,10 @@ class Keyboard: new_width = (key_width * 3 + h_space * 2) if key == SPACE_KEY else (key_width * 2 + h_space if key == ENTER_KEY else key_width) key_rect = rl.Rectangle(start_x, row_y_start + row * (key_height + v_space), new_width, key_height) + if (key_rect.x + key_rect.width) > (rect.x + rect.width): + key_rect.width = rect.x + rect.width - key_rect.x + new_width = key_rect.width + start_x += new_width is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size From cf7a295a2c0487d84f709298f6ae519a40407954 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 20 May 2025 18:01:31 +0800 Subject: [PATCH 290/477] system/ui: add key repeat functionality for backspace (#35288) add key repeat functionality for backspace Co-authored-by: Cameron Clough --- system/ui/widgets/keyboard.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index dfe14e7f40..2eda27b653 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -8,6 +8,8 @@ from openpilot.system.ui.lib.label import gui_label KEY_FONT_SIZE = 96 DOUBLE_CLICK_THRESHOLD = 0.5 # seconds +DELETE_REPEAT_DELAY = 0.5 +DELETE_REPEAT_INTERVAL = 0.07 # Constants for special keys CONTENT_MARGIN = 50 @@ -62,6 +64,11 @@ class Keyboard: self._password_mode = password_mode self._show_password_toggle = show_password_toggle + # Backspace key repeat tracking + self._backspace_pressed: bool = False + self._backspace_press_time: float = 0.0 + self._backspace_last_repeat:float = 0.0 + self._eye_open_texture = gui_app.texture("icons/eye_open.png", 81, 54) self._eye_closed_texture = gui_app.texture("icons/eye_closed.png", 81, 54) self._key_icons = { @@ -80,6 +87,7 @@ class Keyboard: self._layout_name = "lowercase" self._caps_lock = False self._input_box.clear() + self._backspace_pressed = False def render(self, title: str, sub_title: str): rect = rl.Rectangle(CONTENT_MARGIN, CONTENT_MARGIN, gui_app.width - 2 * CONTENT_MARGIN, gui_app.height - 2 * CONTENT_MARGIN) @@ -94,6 +102,21 @@ class Keyboard: input_box_rect = rl.Rectangle(rect.x + input_margin, rect.y + 160, rect.width - input_margin, 100) self._render_input_area(input_box_rect) + # Process backspace key repeat if it's held down + if not rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): + self._backspace_pressed = False + + if self._backspace_pressed: + current_time = time.monotonic() + time_since_press = current_time - self._backspace_press_time + + # After initial delay, start repeating with shorter intervals + if time_since_press > DELETE_REPEAT_DELAY: + time_since_last_repeat = current_time - self._backspace_last_repeat + if time_since_last_repeat > DELETE_REPEAT_INTERVAL: + self._input_box.delete_char_before_cursor() + self._backspace_last_repeat = current_time + layout = KEYBOARD_LAYOUTS[self._layout_name] h_space, v_space = 15, 15 @@ -120,6 +143,17 @@ class Keyboard: is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size result = -1 + + # Check for backspace key press-and-hold + mouse_pos = rl.get_mouse_position() + mouse_over_key = rl.check_collision_point_rec(mouse_pos, key_rect) + + if key == BACKSPACE_KEY and mouse_over_key: + if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + self._backspace_pressed = True + self._backspace_press_time = time.monotonic() + self._backspace_last_repeat = time.monotonic() + if key in self._key_icons: if key == SHIFT_ACTIVE_KEY and self._caps_lock: key = CAPS_LOCK_KEY From 69799fceb4c59c5c10ba30b511408130630e82aa Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 20 May 2025 14:54:06 +0100 Subject: [PATCH 291/477] Revert "system/ui: remove special currency and symbol characters from keyboard (#35291)" This reverts commit f646b4a17ba4cd7e0e396cce250539ea9838eecb. --- system/ui/widgets/keyboard.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 2eda27b653..e940f75d2e 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -45,7 +45,7 @@ KEYBOARD_LAYOUTS = { ], "specials": [ ["[", "]", "{", "}", "#", "%", "^", "*", "+", "="], - ["_", "\\", "|", "~", "<", ">"], + ["_", "\\", "|", "~", "<", ">", "€", "£", "¥", "•"], [NUMERIC_KEY, ".", ",", "?", "!", "'", BACKSPACE_KEY], [ABC_KEY, SPACE_KEY, ".", ENTER_KEY], ], @@ -135,10 +135,6 @@ class Keyboard: new_width = (key_width * 3 + h_space * 2) if key == SPACE_KEY else (key_width * 2 + h_space if key == ENTER_KEY else key_width) key_rect = rl.Rectangle(start_x, row_y_start + row * (key_height + v_space), new_width, key_height) - if (key_rect.x + key_rect.width) > (rect.x + rect.width): - key_rect.width = rect.x + rect.width - key_rect.x - new_width = key_rect.width - start_x += new_width is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size From 566758319828471fc3d2c541957dea22f1e224c1 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 20 May 2025 15:47:10 +0100 Subject: [PATCH 292/477] system/ui: define character set for loading fonts (#35298) * system/ui: define font character set * remove debug print statement * shorter --- system/ui/lib/application.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 8639a0bda5..4d7ba6d24e 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -190,12 +190,23 @@ class GuiApplication: "Inter-Black.ttf", ) + # Create a character set from our keyboard layouts + from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS + all_chars = "" + for layout in KEYBOARD_LAYOUTS.values(): + all_chars.update(key for row in layout for key in row) + all_chars = "".join(all_chars) + + codepoint_count = rl.ffi.new("int *", 1) + codepoints = rl.load_codepoints(all_chars, codepoint_count) + for index, font_file in enumerate(font_files): with as_file(FONT_DIR.joinpath(font_file)) as fspath: - font = rl.load_font_ex(fspath.as_posix(), 120, None, 0) + font = rl.load_font_ex(fspath.as_posix(), 120, codepoints, codepoint_count[0]) rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) self._fonts[index] = font + rl.unload_codepoints(codepoints) rl.gui_set_font(self._fonts[FontWeight.NORMAL]) def _set_styles(self): From 2722e12e6252049858dd1b3efbd98cb5d064a625 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 20 May 2025 15:48:44 +0100 Subject: [PATCH 293/477] fix(system/ui): typo --- system/ui/lib/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 4d7ba6d24e..37da413806 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -192,7 +192,7 @@ class GuiApplication: # Create a character set from our keyboard layouts from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS - all_chars = "" + all_chars = set() for layout in KEYBOARD_LAYOUTS.values(): all_chars.update(key for row in layout for key in row) all_chars = "".join(all_chars) From beb7f6c2bb80ce53470fd1f88c151c248a643b45 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 21 May 2025 02:23:05 +0800 Subject: [PATCH 294/477] system/ui: fix indentation (#35299) fix indentation --- system/ui/widgets/keyboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index e940f75d2e..7836b28184 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -140,7 +140,7 @@ class Keyboard: is_enabled = key != ENTER_KEY or len(self._input_box.text) >= self._min_text_size result = -1 - # Check for backspace key press-and-hold + # Check for backspace key press-and-hold mouse_pos = rl.get_mouse_position() mouse_over_key = rl.check_collision_point_rec(mouse_pos, key_rect) From 4f512a5c6d9d103e024cb87f724d6f9ccf6f65cf Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 21 May 2025 03:27:08 +0800 Subject: [PATCH 295/477] system/ui: fix navigation key repeat in input box (#35302) fix navigation key repeat in input fields --- system/ui/lib/inputbox.py | 44 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 8e53446b43..78ee03bdbd 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -13,7 +13,7 @@ class InputBox: self._last_key_pressed = 0 self._key_press_time = 0 self._repeat_delay = 30 - self._repeat_rate = 5 + self._repeat_rate = 4 @property def text(self): @@ -119,22 +119,29 @@ class InputBox: self.set_cursor_position(0) def _handle_keyboard_input(self): - """Process keyboard input.""" - key = rl.get_key_pressed() - - # Handle key repeats - if key == self._last_key_pressed and key != 0: - self._key_press_time += 1 - if self._key_press_time > self._repeat_delay and self._key_press_time % self._repeat_rate == 0: - # Process repeated key - pass - else: - return # Skip processing until repeat triggers - else: - self._last_key_pressed = key - self._key_press_time = 0 - # Handle navigation keys + key = rl.get_key_pressed() + if key != 0: + self._process_key(key) + if key in (rl.KEY_LEFT, rl.KEY_RIGHT, rl.KEY_BACKSPACE, rl.KEY_DELETE): + self._last_key_pressed = key + self._key_press_time = 0 + + # Handle repeats for held keys + elif self._last_key_pressed != 0: + if rl.is_key_down(self._last_key_pressed): + self._key_press_time += 1 + if self._key_press_time > self._repeat_delay and self._key_press_time % self._repeat_rate == 0: + self._process_key(self._last_key_pressed) + else: + self._last_key_pressed = 0 + + # Handle text input + char = rl.get_char_pressed() + if char != 0 and char >= 32: # Filter out control characters + self.add_char_at_cursor(chr(char)) + + def _process_key(self, key): if key == rl.KEY_LEFT: if self._cursor_position > 0: self.set_cursor_position(self._cursor_position - 1) @@ -149,8 +156,3 @@ class InputBox: self.set_cursor_position(0) elif key == rl.KEY_END: self.set_cursor_position(len(self._input_text)) - - # Handle text input - char = rl.get_char_pressed() - if char != 0 and char >= 32: # Filter out control characters - self.add_char_at_cursor(chr(char)) From e47d105b97c0f41812bc892e8f4157dcb48a5770 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 20 May 2025 14:28:30 -0700 Subject: [PATCH 296/477] revert extra GPU power draw to prep for release --- selfdrive/modeld/dmonitoringmodeld.py | 3 --- system/hardware/tici/tests/test_power_draw.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/selfdrive/modeld/dmonitoringmodeld.py b/selfdrive/modeld/dmonitoringmodeld.py index 94e117bc6a..2a3df3f652 100755 --- a/selfdrive/modeld/dmonitoringmodeld.py +++ b/selfdrive/modeld/dmonitoringmodeld.py @@ -173,9 +173,6 @@ def main(): model_output, gpu_execution_time = model.run(buf, calib, model_transform) t2 = time.perf_counter() - # run one more time, just for the load - model.run(buf, calib, model_transform) - pm.send("driverStateV2", get_driverstate_packet(model_output, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, gpu_execution_time)) diff --git a/system/hardware/tici/tests/test_power_draw.py b/system/hardware/tici/tests/test_power_draw.py index fd65ecd5cd..db0fab884c 100644 --- a/system/hardware/tici/tests/test_power_draw.py +++ b/system/hardware/tici/tests/test_power_draw.py @@ -33,7 +33,7 @@ class Proc: PROCS = [ Proc(['camerad'], 1.75, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']), Proc(['modeld'], 1.12, atol=0.2, msgs=['modelV2']), - Proc(['dmonitoringmodeld'], 1.4, msgs=['driverStateV2']), + Proc(['dmonitoringmodeld'], 0.6, msgs=['driverStateV2']), Proc(['encoderd'], 0.23, msgs=[]), ] From f5cf062a38d722d0f69dc9b5c66f58fd9c110d9c Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Tue, 20 May 2025 14:57:13 -0700 Subject: [PATCH 297/477] [bot] Update Python packages (#35303) Update Python packages Co-authored-by: Vehicle Researcher --- opendbc_repo | 2 +- panda | 2 +- tinygrad_repo | 2 +- uv.lock | 86 +++++++++++++++++++++++++-------------------------- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 447381dee2..0676642f40 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 447381dee2d30fb46e4bf1aec0529667d0b9d159 +Subproject commit 0676642f4029b02758405b3052d1ef8ad2a5528d diff --git a/panda b/panda index 471a455789..871f065e97 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 471a455789b88a0bf1098e87c49d564da266e890 +Subproject commit 871f065e97caf2d85c909d69f38939a79d8563b6 diff --git a/tinygrad_repo b/tinygrad_repo index f9a5ad24c5..2895198c36 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit f9a5ad24c534c0fe3cb2ab8a307b2cc71efc13ea +Subproject commit 2895198c368e226e2176aae37d5e545691ce85cb diff --git a/uv.lock b/uv.lock index 0c4169e0bf..2f8cf68094 100644 --- a/uv.lock +++ b/uv.lock @@ -1103,45 +1103,45 @@ wheels = [ [[package]] name = "multidict" -version = "6.4.3" +version = "6.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/2c/e367dfb4c6538614a0c9453e510d75d66099edf1c4e69da1b5ce691a1931/multidict-6.4.3.tar.gz", hash = "sha256:3ada0b058c9f213c5f95ba301f922d402ac234f1111a7d8fd70f1b99f3c281ec", size = 89372, upload-time = "2025-04-10T22:20:17.956Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/2f/a3470242707058fe856fe59241eee5635d79087100b7042a867368863a27/multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8", size = 90183, upload-time = "2025-05-19T14:16:37.381Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/e0/53cf7f27eda48fffa53cfd4502329ed29e00efb9e4ce41362cbf8aa54310/multidict-6.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6f19170197cc29baccd33ccc5b5d6a331058796485857cf34f7635aa25fb0cd", size = 65259, upload-time = "2025-04-10T22:17:59.632Z" }, - { url = "https://files.pythonhosted.org/packages/44/79/1dcd93ce7070cf01c2ee29f781c42b33c64fce20033808f1cc9ec8413d6e/multidict-6.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2882bf27037eb687e49591690e5d491e677272964f9ec7bc2abbe09108bdfb8", size = 38451, upload-time = "2025-04-10T22:18:01.202Z" }, - { url = "https://files.pythonhosted.org/packages/f4/35/2292cf29ab5f0d0b3613fad1b75692148959d3834d806be1885ceb49a8ff/multidict-6.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbf226ac85f7d6b6b9ba77db4ec0704fde88463dc17717aec78ec3c8546c70ad", size = 37706, upload-time = "2025-04-10T22:18:02.276Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d1/6b157110b2b187b5a608b37714acb15ee89ec773e3800315b0107ea648cd/multidict-6.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e329114f82ad4b9dd291bef614ea8971ec119ecd0f54795109976de75c9a852", size = 226669, upload-time = "2025-04-10T22:18:03.436Z" }, - { url = "https://files.pythonhosted.org/packages/40/7f/61a476450651f177c5570e04bd55947f693077ba7804fe9717ee9ae8de04/multidict-6.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f4e0334d7a555c63f5c8952c57ab6f1c7b4f8c7f3442df689fc9f03df315c08", size = 223182, upload-time = "2025-04-10T22:18:04.922Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/eaf7502ac4824cdd8edcf5723e2e99f390c879866aec7b0c420267b53749/multidict-6.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:740915eb776617b57142ce0bb13b7596933496e2f798d3d15a20614adf30d229", size = 235025, upload-time = "2025-04-10T22:18:06.274Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f6/facdbbd73c96b67a93652774edd5778ab1167854fa08ea35ad004b1b70ad/multidict-6.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255dac25134d2b141c944b59a0d2f7211ca12a6d4779f7586a98b4b03ea80508", size = 231481, upload-time = "2025-04-10T22:18:07.742Z" }, - { url = "https://files.pythonhosted.org/packages/70/57/c008e861b3052405eebf921fd56a748322d8c44dcfcab164fffbccbdcdc4/multidict-6.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4e8535bd4d741039b5aad4285ecd9b902ef9e224711f0b6afda6e38d7ac02c7", size = 223492, upload-time = "2025-04-10T22:18:09.095Z" }, - { url = "https://files.pythonhosted.org/packages/30/4d/7d8440d3a12a6ae5d6b202d6e7f2ac6ab026e04e99aaf1b73f18e6bc34bc/multidict-6.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c433a33be000dd968f5750722eaa0991037be0be4a9d453eba121774985bc8", size = 217279, upload-time = "2025-04-10T22:18:10.474Z" }, - { url = "https://files.pythonhosted.org/packages/7f/e7/bca0df4dd057597b94138d2d8af04eb3c27396a425b1b0a52e082f9be621/multidict-6.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4eb33b0bdc50acd538f45041f5f19945a1f32b909b76d7b117c0c25d8063df56", size = 228733, upload-time = "2025-04-10T22:18:11.793Z" }, - { url = "https://files.pythonhosted.org/packages/88/f5/383827c3f1c38d7c92dbad00a8a041760228573b1c542fbf245c37bbca8a/multidict-6.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:75482f43465edefd8a5d72724887ccdcd0c83778ded8f0cb1e0594bf71736cc0", size = 218089, upload-time = "2025-04-10T22:18:13.153Z" }, - { url = "https://files.pythonhosted.org/packages/36/8a/a5174e8a7d8b94b4c8f9c1e2cf5d07451f41368ffe94d05fc957215b8e72/multidict-6.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce5b3082e86aee80b3925ab4928198450d8e5b6466e11501fe03ad2191c6d777", size = 225257, upload-time = "2025-04-10T22:18:14.654Z" }, - { url = "https://files.pythonhosted.org/packages/8c/76/1d4b7218f0fd00b8e5c90b88df2e45f8af127f652f4e41add947fa54c1c4/multidict-6.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e413152e3212c4d39f82cf83c6f91be44bec9ddea950ce17af87fbf4e32ca6b2", size = 234728, upload-time = "2025-04-10T22:18:16.236Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/18372a4f6273fc7ca25630d7bf9ae288cde64f29593a078bff450c7170b6/multidict-6.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8aac2eeff69b71f229a405c0a4b61b54bade8e10163bc7b44fcd257949620618", size = 230087, upload-time = "2025-04-10T22:18:17.979Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/28728c314a698d8a6d9491fcacc897077348ec28dd85884d09e64df8a855/multidict-6.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab583ac203af1d09034be41458feeab7863c0635c650a16f15771e1386abf2d7", size = 223137, upload-time = "2025-04-10T22:18:19.362Z" }, - { url = "https://files.pythonhosted.org/packages/22/50/785bb2b3fe16051bc91c70a06a919f26312da45c34db97fc87441d61e343/multidict-6.4.3-cp311-cp311-win32.whl", hash = "sha256:1b2019317726f41e81154df636a897de1bfe9228c3724a433894e44cd2512378", size = 34959, upload-time = "2025-04-10T22:18:20.728Z" }, - { url = "https://files.pythonhosted.org/packages/2f/63/2a22e099ae2f4d92897618c00c73a09a08a2a9aa14b12736965bf8d59fd3/multidict-6.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:43173924fa93c7486402217fab99b60baf78d33806af299c56133a3755f69589", size = 38541, upload-time = "2025-04-10T22:18:22.001Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bb/3abdaf8fe40e9226ce8a2ba5ecf332461f7beec478a455d6587159f1bf92/multidict-6.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f1c2f58f08b36f8475f3ec6f5aeb95270921d418bf18f90dffd6be5c7b0e676", size = 64019, upload-time = "2025-04-10T22:18:23.174Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b5/1b2e8de8217d2e89db156625aa0fe4a6faad98972bfe07a7b8c10ef5dd6b/multidict-6.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:26ae9ad364fc61b936fb7bf4c9d8bd53f3a5b4417142cd0be5c509d6f767e2f1", size = 37925, upload-time = "2025-04-10T22:18:24.834Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e2/3ca91c112644a395c8eae017144c907d173ea910c913ff8b62549dcf0bbf/multidict-6.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:659318c6c8a85f6ecfc06b4e57529e5a78dfdd697260cc81f683492ad7e9435a", size = 37008, upload-time = "2025-04-10T22:18:26.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/23/79bc78146c7ac8d1ac766b2770ca2e07c2816058b8a3d5da6caed8148637/multidict-6.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1eb72c741fd24d5a28242ce72bb61bc91f8451877131fa3fe930edb195f7054", size = 224374, upload-time = "2025-04-10T22:18:27.714Z" }, - { url = "https://files.pythonhosted.org/packages/86/35/77950ed9ebd09136003a85c1926ba42001ca5be14feb49710e4334ee199b/multidict-6.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cd06d88cb7398252284ee75c8db8e680aa0d321451132d0dba12bc995f0adcc", size = 230869, upload-time = "2025-04-10T22:18:29.162Z" }, - { url = "https://files.pythonhosted.org/packages/49/97/2a33c6e7d90bc116c636c14b2abab93d6521c0c052d24bfcc231cbf7f0e7/multidict-6.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4543d8dc6470a82fde92b035a92529317191ce993533c3c0c68f56811164ed07", size = 231949, upload-time = "2025-04-10T22:18:30.679Z" }, - { url = "https://files.pythonhosted.org/packages/56/ce/e9b5d9fcf854f61d6686ada7ff64893a7a5523b2a07da6f1265eaaea5151/multidict-6.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30a3ebdc068c27e9d6081fca0e2c33fdf132ecea703a72ea216b81a66860adde", size = 231032, upload-time = "2025-04-10T22:18:32.146Z" }, - { url = "https://files.pythonhosted.org/packages/f0/ac/7ced59dcdfeddd03e601edb05adff0c66d81ed4a5160c443e44f2379eef0/multidict-6.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b038f10e23f277153f86f95c777ba1958bcd5993194fda26a1d06fae98b2f00c", size = 223517, upload-time = "2025-04-10T22:18:33.538Z" }, - { url = "https://files.pythonhosted.org/packages/db/e6/325ed9055ae4e085315193a1b58bdb4d7fc38ffcc1f4975cfca97d015e17/multidict-6.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c605a2b2dc14282b580454b9b5d14ebe0668381a3a26d0ac39daa0ca115eb2ae", size = 216291, upload-time = "2025-04-10T22:18:34.962Z" }, - { url = "https://files.pythonhosted.org/packages/fa/84/eeee6d477dd9dcb7691c3bb9d08df56017f5dd15c730bcc9383dcf201cf4/multidict-6.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8bd2b875f4ca2bb527fe23e318ddd509b7df163407b0fb717df229041c6df5d3", size = 228982, upload-time = "2025-04-10T22:18:36.443Z" }, - { url = "https://files.pythonhosted.org/packages/82/94/4d1f3e74e7acf8b0c85db350e012dcc61701cd6668bc2440bb1ecb423c90/multidict-6.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c2e98c840c9c8e65c0e04b40c6c5066c8632678cd50c8721fdbcd2e09f21a507", size = 226823, upload-time = "2025-04-10T22:18:37.924Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/1e54b95bda7cd01080e5732f9abb7b76ab5cc795b66605877caeb2197476/multidict-6.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66eb80dd0ab36dbd559635e62fba3083a48a252633164857a1d1684f14326427", size = 222714, upload-time = "2025-04-10T22:18:39.807Z" }, - { url = "https://files.pythonhosted.org/packages/e7/a2/f6cbca875195bd65a3e53b37ab46486f3cc125bdeab20eefe5042afa31fb/multidict-6.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c23831bdee0a2a3cf21be057b5e5326292f60472fb6c6f86392bbf0de70ba731", size = 233739, upload-time = "2025-04-10T22:18:41.341Z" }, - { url = "https://files.pythonhosted.org/packages/79/68/9891f4d2b8569554723ddd6154375295f789dc65809826c6fb96a06314fd/multidict-6.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1535cec6443bfd80d028052e9d17ba6ff8a5a3534c51d285ba56c18af97e9713", size = 230809, upload-time = "2025-04-10T22:18:42.817Z" }, - { url = "https://files.pythonhosted.org/packages/e6/72/a7be29ba1e87e4fc5ceb44dabc7940b8005fd2436a332a23547709315f70/multidict-6.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3b73e7227681f85d19dec46e5b881827cd354aabe46049e1a61d2f9aaa4e285a", size = 226934, upload-time = "2025-04-10T22:18:44.311Z" }, - { url = "https://files.pythonhosted.org/packages/12/c1/259386a9ad6840ff7afc686da96808b503d152ac4feb3a96c651dc4f5abf/multidict-6.4.3-cp312-cp312-win32.whl", hash = "sha256:8eac0c49df91b88bf91f818e0a24c1c46f3622978e2c27035bfdca98e0e18124", size = 35242, upload-time = "2025-04-10T22:18:46.193Z" }, - { url = "https://files.pythonhosted.org/packages/06/24/c8fdff4f924d37225dc0c56a28b1dca10728fc2233065fafeb27b4b125be/multidict-6.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:11990b5c757d956cd1db7cb140be50a63216af32cd6506329c2c59d732d802db", size = 38635, upload-time = "2025-04-10T22:18:47.498Z" }, - { url = "https://files.pythonhosted.org/packages/96/10/7d526c8974f017f1e7ca584c71ee62a638e9334d8d33f27d7cdfc9ae79e4/multidict-6.4.3-py3-none-any.whl", hash = "sha256:59fe01ee8e2a1e8ceb3f6dbb216b09c8d9f4ef1c22c4fc825d045a147fa2ebc9", size = 10400, upload-time = "2025-04-10T22:20:16.445Z" }, + { url = "https://files.pythonhosted.org/packages/19/1b/4c6e638195851524a63972c5773c7737bea7e47b1ba402186a37773acee2/multidict-6.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f5f29794ac0e73d2a06ac03fd18870adc0135a9d384f4a306a951188ed02f95", size = 65515, upload-time = "2025-05-19T14:14:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/10e6bca9a44b8af3c7f920743e5fc0c2bcf8c11bf7a295d4cfe00b08fb46/multidict-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04157266344158ebd57b7120d9b0b35812285d26d0e78193e17ef57bfe2979a", size = 38609, upload-time = "2025-05-19T14:14:21.538Z" }, + { url = "https://files.pythonhosted.org/packages/26/b4/91fead447ccff56247edc7f0535fbf140733ae25187a33621771ee598a18/multidict-6.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb61ffd3ab8310d93427e460f565322c44ef12769f51f77277b4abad7b6f7223", size = 37871, upload-time = "2025-05-19T14:14:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/3b/37/cbc977cae59277e99d15bbda84cc53b5e0c4929ffd91d958347200a42ad0/multidict-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0ba18a9afd495f17c351d08ebbc4284e9c9f7971d715f196b79636a4d0de44", size = 226661, upload-time = "2025-05-19T14:14:24.124Z" }, + { url = "https://files.pythonhosted.org/packages/15/cd/7e0b57fbd4dc2fc105169c4ecce5be1a63970f23bb4ec8c721b67e11953d/multidict-6.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9faf1b1dcaadf9f900d23a0e6d6c8eadd6a95795a0e57fcca73acce0eb912065", size = 223422, upload-time = "2025-05-19T14:14:25.437Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/1de268da121bac9f93242e30cd3286f6a819e5f0b8896511162d6ed4bf8d/multidict-6.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a4d1cb1327c6082c4fce4e2a438483390964c02213bc6b8d782cf782c9b1471f", size = 235447, upload-time = "2025-05-19T14:14:26.793Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8c/8b9a5e4aaaf4f2de14e86181a3a3d7b105077f668b6a06f043ec794f684c/multidict-6.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:941f1bec2f5dbd51feeb40aea654c2747f811ab01bdd3422a48a4e4576b7d76a", size = 231455, upload-time = "2025-05-19T14:14:28.149Z" }, + { url = "https://files.pythonhosted.org/packages/35/db/e1817dcbaa10b319c412769cf999b1016890849245d38905b73e9c286862/multidict-6.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5f8a146184da7ea12910a4cec51ef85e44f6268467fb489c3caf0cd512f29c2", size = 223666, upload-time = "2025-05-19T14:14:29.584Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e1/66e8579290ade8a00e0126b3d9a93029033ffd84f0e697d457ed1814d0fc/multidict-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232b7237e57ec3c09be97206bfb83a0aa1c5d7d377faa019c68a210fa35831f1", size = 217392, upload-time = "2025-05-19T14:14:30.961Z" }, + { url = "https://files.pythonhosted.org/packages/7b/6f/f8639326069c24a48c7747c2a5485d37847e142a3f741ff3340c88060a9a/multidict-6.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:55ae0721c1513e5e3210bca4fc98456b980b0c2c016679d3d723119b6b202c42", size = 228969, upload-time = "2025-05-19T14:14:32.672Z" }, + { url = "https://files.pythonhosted.org/packages/d2/c3/3d58182f76b960eeade51c89fcdce450f93379340457a328e132e2f8f9ed/multidict-6.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:51d662c072579f63137919d7bb8fc250655ce79f00c82ecf11cab678f335062e", size = 217433, upload-time = "2025-05-19T14:14:34.016Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4b/f31a562906f3bd375f3d0e83ce314e4a660c01b16c2923e8229b53fba5d7/multidict-6.4.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0e05c39962baa0bb19a6b210e9b1422c35c093b651d64246b6c2e1a7e242d9fd", size = 225418, upload-time = "2025-05-19T14:14:35.376Z" }, + { url = "https://files.pythonhosted.org/packages/99/89/78bb95c89c496d64b5798434a3deee21996114d4d2c28dd65850bf3a691e/multidict-6.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b1cc3ab8c31d9ebf0faa6e3540fb91257590da330ffe6d2393d4208e638925", size = 235042, upload-time = "2025-05-19T14:14:36.723Z" }, + { url = "https://files.pythonhosted.org/packages/74/91/8780a6e5885a8770442a8f80db86a0887c4becca0e5a2282ba2cae702bc4/multidict-6.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93ec84488a384cd7b8a29c2c7f467137d8a73f6fe38bb810ecf29d1ade011a7c", size = 230280, upload-time = "2025-05-19T14:14:38.194Z" }, + { url = "https://files.pythonhosted.org/packages/68/c1/fcf69cabd542eb6f4b892469e033567ee6991d361d77abdc55e3a0f48349/multidict-6.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b308402608493638763abc95f9dc0030bbd6ac6aff784512e8ac3da73a88af08", size = 223322, upload-time = "2025-05-19T14:14:40.015Z" }, + { url = "https://files.pythonhosted.org/packages/b8/85/5b80bf4b83d8141bd763e1d99142a9cdfd0db83f0739b4797172a4508014/multidict-6.4.4-cp311-cp311-win32.whl", hash = "sha256:343892a27d1a04d6ae455ecece12904d242d299ada01633d94c4f431d68a8c49", size = 35070, upload-time = "2025-05-19T14:14:41.904Z" }, + { url = "https://files.pythonhosted.org/packages/09/66/0bed198ffd590ab86e001f7fa46b740d58cf8ff98c2f254e4a36bf8861ad/multidict-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:73484a94f55359780c0f458bbd3c39cb9cf9c182552177d2136e828269dee529", size = 38667, upload-time = "2025-05-19T14:14:43.534Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b5/5675377da23d60875fe7dae6be841787755878e315e2f517235f22f59e18/multidict-6.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc388f75a1c00000824bf28b7633e40854f4127ede80512b44c3cfeeea1839a2", size = 64293, upload-time = "2025-05-19T14:14:44.724Z" }, + { url = "https://files.pythonhosted.org/packages/34/a7/be384a482754bb8c95d2bbe91717bf7ccce6dc38c18569997a11f95aa554/multidict-6.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:98af87593a666f739d9dba5d0ae86e01b0e1a9cfcd2e30d2d361fbbbd1a9162d", size = 38096, upload-time = "2025-05-19T14:14:45.95Z" }, + { url = "https://files.pythonhosted.org/packages/66/6d/d59854bb4352306145bdfd1704d210731c1bb2c890bfee31fb7bbc1c4c7f/multidict-6.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aff4cafea2d120327d55eadd6b7f1136a8e5a0ecf6fb3b6863e8aca32cd8e50a", size = 37214, upload-time = "2025-05-19T14:14:47.158Z" }, + { url = "https://files.pythonhosted.org/packages/99/e0/c29d9d462d7cfc5fc8f9bf24f9c6843b40e953c0b55e04eba2ad2cf54fba/multidict-6.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:169c4ba7858176b797fe551d6e99040c531c775d2d57b31bcf4de6d7a669847f", size = 224686, upload-time = "2025-05-19T14:14:48.366Z" }, + { url = "https://files.pythonhosted.org/packages/dc/4a/da99398d7fd8210d9de068f9a1b5f96dfaf67d51e3f2521f17cba4ee1012/multidict-6.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9eb4c59c54421a32b3273d4239865cb14ead53a606db066d7130ac80cc8ec93", size = 231061, upload-time = "2025-05-19T14:14:49.952Z" }, + { url = "https://files.pythonhosted.org/packages/21/f5/ac11add39a0f447ac89353e6ca46666847051103649831c08a2800a14455/multidict-6.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cf3bd54c56aa16fdb40028d545eaa8d051402b61533c21e84046e05513d5780", size = 232412, upload-time = "2025-05-19T14:14:51.812Z" }, + { url = "https://files.pythonhosted.org/packages/d9/11/4b551e2110cded705a3c13a1d4b6a11f73891eb5a1c449f1b2b6259e58a6/multidict-6.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f682c42003c7264134bfe886376299db4cc0c6cd06a3295b41b347044bcb5482", size = 231563, upload-time = "2025-05-19T14:14:53.262Z" }, + { url = "https://files.pythonhosted.org/packages/4c/02/751530c19e78fe73b24c3da66618eda0aa0d7f6e7aa512e46483de6be210/multidict-6.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920f9cf2abdf6e493c519492d892c362007f113c94da4c239ae88429835bad1", size = 223811, upload-time = "2025-05-19T14:14:55.232Z" }, + { url = "https://files.pythonhosted.org/packages/c7/cb/2be8a214643056289e51ca356026c7b2ce7225373e7a1f8c8715efee8988/multidict-6.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:530d86827a2df6504526106b4c104ba19044594f8722d3e87714e847c74a0275", size = 216524, upload-time = "2025-05-19T14:14:57.226Z" }, + { url = "https://files.pythonhosted.org/packages/19/f3/6d5011ec375c09081f5250af58de85f172bfcaafebff286d8089243c4bd4/multidict-6.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ecde56ea2439b96ed8a8d826b50c57364612ddac0438c39e473fafad7ae1c23b", size = 229012, upload-time = "2025-05-19T14:14:58.597Z" }, + { url = "https://files.pythonhosted.org/packages/67/9c/ca510785df5cf0eaf5b2a8132d7d04c1ce058dcf2c16233e596ce37a7f8e/multidict-6.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:dc8c9736d8574b560634775ac0def6bdc1661fc63fa27ffdfc7264c565bcb4f2", size = 226765, upload-time = "2025-05-19T14:15:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/36/c8/ca86019994e92a0f11e642bda31265854e6ea7b235642f0477e8c2e25c1f/multidict-6.4.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7f3d3b3c34867579ea47cbd6c1f2ce23fbfd20a273b6f9e3177e256584f1eacc", size = 222888, upload-time = "2025-05-19T14:15:01.568Z" }, + { url = "https://files.pythonhosted.org/packages/c6/67/bc25a8e8bd522935379066950ec4e2277f9b236162a73548a2576d4b9587/multidict-6.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:87a728af265e08f96b6318ebe3c0f68b9335131f461efab2fc64cc84a44aa6ed", size = 234041, upload-time = "2025-05-19T14:15:03.759Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a0/70c4c2d12857fccbe607b334b7ee28b6b5326c322ca8f73ee54e70d76484/multidict-6.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9f193eeda1857f8e8d3079a4abd258f42ef4a4bc87388452ed1e1c4d2b0c8740", size = 231046, upload-time = "2025-05-19T14:15:05.698Z" }, + { url = "https://files.pythonhosted.org/packages/c1/0f/52954601d02d39742aab01d6b92f53c1dd38b2392248154c50797b4df7f1/multidict-6.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be06e73c06415199200e9a2324a11252a3d62030319919cde5e6950ffeccf72e", size = 227106, upload-time = "2025-05-19T14:15:07.124Z" }, + { url = "https://files.pythonhosted.org/packages/af/24/679d83ec4379402d28721790dce818e5d6b9f94ce1323a556fb17fa9996c/multidict-6.4.4-cp312-cp312-win32.whl", hash = "sha256:622f26ea6a7e19b7c48dd9228071f571b2fbbd57a8cd71c061e848f281550e6b", size = 35351, upload-time = "2025-05-19T14:15:08.556Z" }, + { url = "https://files.pythonhosted.org/packages/52/ef/40d98bc5f986f61565f9b345f102409534e29da86a6454eb6b7c00225a13/multidict-6.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:5e2bcda30d5009996ff439e02a9f2b5c3d64a20151d34898c000a6281faa3781", size = 38791, upload-time = "2025-05-19T14:15:09.825Z" }, + { url = "https://files.pythonhosted.org/packages/84/5d/e17845bb0fa76334477d5de38654d27946d5b5d3695443987a094a71b440/multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac", size = 10481, upload-time = "2025-05-19T14:16:36.024Z" }, ] [[package]] @@ -4779,15 +4779,15 @@ wheels = [ [[package]] name = "sentry-sdk" -version = "2.29.0" +version = "2.29.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/ec/9a313e221bb24c162f176cd7e04634f5980ce00a431befc2b2e0f78669d2/sentry_sdk-2.29.0.tar.gz", hash = "sha256:a7c6c77c46c12492c41dd70b29d481daa67cc173618c8fa60792737dbefc516b", size = 325543, upload-time = "2025-05-19T13:46:07.31Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/67/d552a5f8e5a6a56b2feea6529e2d8ccd54349084c84176d5a1f7295044bc/sentry_sdk-2.29.1.tar.gz", hash = "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d", size = 325518, upload-time = "2025-05-19T14:27:38.512Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/cf/fa451f0518f76b21f209a71684738a3c9aa0e36ead786a5f806c3092e647/sentry_sdk-2.29.0-py2.py3-none-any.whl", hash = "sha256:5234404e2208acbce051e374f0cfc4727d69b33c5aafa5109056949b5b847c92", size = 341552, upload-time = "2025-05-19T13:46:05.067Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e5/da07b0bd832cefd52d16f2b9bbbe31624d57552602c06631686b93ccb1bd/sentry_sdk-2.29.1-py2.py3-none-any.whl", hash = "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19", size = 341553, upload-time = "2025-05-19T14:27:36.882Z" }, ] [[package]] @@ -4824,11 +4824,11 @@ wheels = [ [[package]] name = "setuptools" -version = "80.7.1" +version = "80.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/8b/dc1773e8e5d07fd27c1632c45c1de856ac3dbf09c0147f782ca6d990cf15/setuptools-80.7.1.tar.gz", hash = "sha256:f6ffc5f0142b1bd8d0ca94ee91b30c0ca862ffd50826da1ea85258a06fd94552", size = 1319188, upload-time = "2025-05-15T02:41:00.955Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/d2/ec1acaaff45caed5c2dedb33b67055ba9d4e96b091094df90762e60135fe/setuptools-80.8.0.tar.gz", hash = "sha256:49f7af965996f26d43c8ae34539c8d99c5042fbff34302ea151eaa9c207cd257", size = 1319720, upload-time = "2025-05-20T14:02:53.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/18/0e835c3a557dc5faffc8f91092f62fc337c1dab1066715842e7a4b318ec4/setuptools-80.7.1-py3-none-any.whl", hash = "sha256:ca5cc1069b85dc23070a6628e6bcecb3292acac802399c7f8edc0100619f9009", size = 1200776, upload-time = "2025-05-15T02:40:58.887Z" }, + { url = "https://files.pythonhosted.org/packages/58/29/93c53c098d301132196c3238c312825324740851d77a8500a2462c0fd888/setuptools-80.8.0-py3-none-any.whl", hash = "sha256:95a60484590d24103af13b686121328cc2736bee85de8936383111e421b9edc0", size = 1201470, upload-time = "2025-05-20T14:02:51.348Z" }, ] [[package]] From ac9222bff48cc10f71aba95fc7bfc3a979dfcf7e Mon Sep 17 00:00:00 2001 From: Ocheretovich Date: Wed, 21 May 2025 00:57:26 +0300 Subject: [PATCH 298/477] docs: added a link to the selfdrive badge (#35300) Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0248b0f54..86cccafad9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Quick start: `bash <(curl -fsSL openpilot.comma.ai)` -![openpilot tests](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml/badge.svg) +[![openpilot tests](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml/badge.svg)](https://github.com/commaai/openpilot/actions/workflows/selfdrive_tests.yaml) [![codecov](https://codecov.io/gh/commaai/openpilot/branch/master/graph/badge.svg)](https://codecov.io/gh/commaai/openpilot) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![X Follow](https://img.shields.io/twitter/follow/comma_ai)](https://x.com/comma_ai) From 10fd0f9a5725bd9a7d514c142a8e0232d3480688 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 20 May 2025 16:45:33 -0700 Subject: [PATCH 299/477] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 871f065e97..d75699e6ab 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 871f065e97caf2d85c909d69f38939a79d8563b6 +Subproject commit d75699e6abf41636701775ba0eea8e07858599ad From 9cd939d35497bc6d96607d4f5de3441501facd6f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 21 May 2025 07:59:48 +0800 Subject: [PATCH 300/477] ui: rewrite installer using raylib, remove qt (#33756) * update SConscript Co-authored-by: Dean Lee * can't build on mac * raylib installer Co-authored-by: Dean Lee * rm * debug pls * cleanup * quotes * libs? * bump * hmm * progress bar color * more * not rounded * hardcode a font path..? * embed (a subset of) inter into the installer * different in CI * closer font sizes * closer * add that back * unnecessary * closer to previous impl * prefix is at start of string, substr between prefix and % * Revert "prefix is at start of string, substr between prefix and %" This reverts commit bc53fe8e356ca642680e90682285bd5e8d98ecb5. * bigger on device? * rm --------- Co-authored-by: Cameron Clough --- selfdrive/ui/SConscript | 26 ++-- selfdrive/ui/installer/installer.cc | 182 +++++++++++-------------- selfdrive/ui/installer/installer.h | 28 ---- selfdrive/ui/installer/inter-ascii.ttf | 3 + 4 files changed, 97 insertions(+), 142 deletions(-) delete mode 100644 selfdrive/ui/installer/installer.h create mode 100644 selfdrive/ui/installer/inter-ascii.ttf diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e63359da05..7199399d41 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,5 @@ -import os import json -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -76,8 +75,15 @@ if GetOption('extras'): if arch != "Darwin": # build installers - senv = qt_env.Clone() - senv['LINKFLAGS'].append('-Wl,-strip-debug') + raylib_env = env.Clone() + raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] + raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') + + raylib_libs = common + ["raylib"] + if arch == "larch64": + raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"] + else: + raylib_libs += ["GL"] release = "release3" installers = [ @@ -87,17 +93,19 @@ if GetOption('extras'): ("openpilot_internal", "nightly-dev"), ] - cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh", - "ld -r -b binary -o $TARGET $SOURCE") + cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", + "ld -r -b binary -o $TARGET $SOURCE") + inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", + "ld -r -b binary -o $TARGET $SOURCE") for name, branch in installers: d = {'BRANCH': f"'\"{branch}\"'"} if "internal" in name: d['INTERNAL'] = "1" - obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) - f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs) + obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) + f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) # keep installers small - assert f[0].get_size() < 370*1e3 + assert f[0].get_size() < 1300*1e3, f[0].get_size() # build watch3 if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'): diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 84486e61be..7326e089ab 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -1,19 +1,15 @@ -#include - -#include +#include +#include #include #include -#include - -#include -#include -#include -#include +#include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/ui/installer/installer.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" +#include "third_party/raylib/include/raylib.h" + +int freshClone(); +int cachedFetch(const std::string &cache); +int executeGitCommand(const std::string &cmd); std::string get_str(std::string const s) { std::string::size_type pos = s.find('?'); @@ -28,136 +24,108 @@ const std::string BRANCH_STR = get_str(BRANCH "? #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" -const QString CACHE_PATH = "/data/openpilot.cache"; +const std::string CACHE_PATH = "/data/openpilot.cache"; #define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); +extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); +extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end"); + +Font font; void run(const char* cmd) { int err = std::system(cmd); assert(err == 0); } -Installer::Installer(QWidget *parent) : QWidget(parent) { - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(150, 290, 150, 150); - layout->setSpacing(0); - - QLabel *title = new QLabel(tr("Installing...")); - title->setStyleSheet("font-size: 90px; font-weight: 600;"); - layout->addWidget(title, 0, Qt::AlignTop); - - layout->addSpacing(170); - - bar = new QProgressBar(); - bar->setRange(0, 100); - bar->setTextVisible(false); - bar->setFixedHeight(72); - layout->addWidget(bar, 0, Qt::AlignTop); - - layout->addSpacing(30); - - val = new QLabel("0%"); - val->setStyleSheet("font-size: 70px; font-weight: 300;"); - layout->addWidget(val, 0, Qt::AlignTop); - - layout->addStretch(); - - QObject::connect(&proc, QOverload::of(&QProcess::finished), this, &Installer::cloneFinished); - QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress); - - QTimer::singleShot(100, this, &Installer::doInstall); - - setStyleSheet(R"( - * { - font-family: Inter; - color: white; - background-color: black; - } - QProgressBar { - border: none; - background-color: #292929; - } - QProgressBar::chunk { - background-color: #364DEF; - } - )"); +void renderProgress(int progress) { + BeginDrawing(); + ClearBackground(BLACK); + DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE); + Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72}; + DrawRectangleRec(bar, (Color){41, 41, 41, 255}); + progress = std::clamp(progress, 0, 100); + bar.width *= progress / 100.0f; + DrawRectangleRec(bar, (Color){70, 91, 234, 255}); + DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE); + EndDrawing(); } -void Installer::updateProgress(int percent) { - bar->setValue(percent); - val->setText(QString("%1%").arg(percent)); - update(); -} - -void Installer::doInstall() { +int doInstall() { // wait for valid time while (!util::system_time_valid()) { - usleep(500 * 1000); - qDebug() << "Waiting for valid time"; + util::sleep_for(500); + LOGD("Waiting for valid time"); } // cleanup previous install attempts run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); // do the install - if (QDir(CACHE_PATH).exists()) { - cachedFetch(CACHE_PATH); + if (util::file_exists(CACHE_PATH)) { + return cachedFetch(CACHE_PATH); } else { - freshClone(); + return freshClone(); } } -void Installer::freshClone() { - qDebug() << "Doing fresh clone"; - proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(), - "--depth=1", "--recurse-submodules", TMP_INSTALL_PATH}); +int freshClone() { + LOGD("Doing fresh clone"); + std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", + GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); + return executeGitCommand(cmd); } -void Installer::cachedFetch(const QString &cache) { - qDebug() << "Fetching with cache: " << cache; +int cachedFetch(const std::string &cache) { + LOGD("Fetching with cache: %s", cache.c_str()); - run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str()); - int err = chdir(TMP_INSTALL_PATH); - assert(err == 0); - run(("git remote set-branches --add origin " + BRANCH_STR).c_str()); + run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); + run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); - updateProgress(10); + renderProgress(10); - proc.setWorkingDirectory(TMP_INSTALL_PATH); - proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()}); + return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); } -void Installer::readProgress() { - const QVector> stages = { +int executeGitCommand(const std::string &cmd) { + static const std::array stages = { // prefix, weight in percentage - {"Receiving objects: ", 91}, - {"Resolving deltas: ", 2}, - {"Updating files: ", 7}, + std::pair{"Receiving objects: ", 91}, + std::pair{"Resolving deltas: ", 2}, + std::pair{"Updating files: ", 7}, }; - auto line = QString(proc.readAllStandardError()); + FILE *pipe = popen(cmd.c_str(), "r"); + if (!pipe) return -1; - int base = 0; - for (const QPair kv : stages) { - if (line.startsWith(kv.first)) { - auto perc = line.split(kv.first)[1].split("%")[0]; - int p = base + int(perc.toFloat() / 100. * kv.second); - updateProgress(p); - break; + char buffer[512]; + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + std::string line(buffer); + int base = 0; + for (const auto &[text, weight] : stages) { + if (line.find(text) != std::string::npos) { + size_t percentPos = line.find("%"); + if (percentPos != std::string::npos && percentPos >= 3) { + int percent = std::stoi(line.substr(percentPos - 3, 3)); + int progress = base + int(percent / 100. * weight); + renderProgress(progress); + } + break; + } + base += weight; } - base += kv.second; } + return pclose(pipe); } -void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "git finished with " << exitCode; +void cloneFinished(int exitCode) { + LOGD("git finished with %d", exitCode); assert(exitCode == 0); - updateProgress(100); + renderProgress(100); // ensure correct branch is checked out int err = chdir(TMP_INSTALL_PATH); @@ -203,13 +171,17 @@ void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over - QTimer::singleShot(60 * 1000, &QCoreApplication::quit); + util::sleep_for(60 * 1000); } int main(int argc, char *argv[]) { - initApp(argc, argv); - QApplication a(argc, argv); - Installer installer; - setMainWindow(&installer); - return a.exec(); + InitWindow(2160, 1080, "Installer"); + font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); + SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + renderProgress(0); + int result = doInstall(); + cloneFinished(result); + CloseWindow(); + UnloadFont(font); + return 0; } diff --git a/selfdrive/ui/installer/installer.h b/selfdrive/ui/installer/installer.h deleted file mode 100644 index de3af0ff39..0000000000 --- a/selfdrive/ui/installer/installer.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class Installer : public QWidget { - Q_OBJECT - -public: - explicit Installer(QWidget *parent = 0); - -private slots: - void updateProgress(int percent); - - void readProgress(); - void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus); - -private: - QLabel *val; - QProgressBar *bar; - QProcess proc; - - void doInstall(); - void freshClone(); - void cachedFetch(const QString &cache); -}; diff --git a/selfdrive/ui/installer/inter-ascii.ttf b/selfdrive/ui/installer/inter-ascii.ttf new file mode 100644 index 0000000000..5d480c515a --- /dev/null +++ b/selfdrive/ui/installer/inter-ascii.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ef26a4099ef867f3493389379d882381a2491cdbfa41a086be8899a2154dcb3 +size 26160 From 6d0cac53051c80c019bc1f03bb027a21ed945037 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 20 May 2025 17:32:27 -0700 Subject: [PATCH 301/477] MultiButtonControl (#35307) * copy * remove params from it * split out --- selfdrive/ui/qt/widgets/controls.h | 37 ++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index aebf934f2a..ca93110e5b 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -183,10 +183,10 @@ private: bool store_confirm = false; }; -class ButtonParamControl : public AbstractControl { +class MultiButtonControl : public AbstractControl { Q_OBJECT public: - ButtonParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + MultiButtonControl(const QString &title, const QString &desc, const QString &icon, const std::vector &button_texts, const int minimum_button_width = 225) : AbstractControl(title, desc, icon) { const QString style = R"( QPushButton { @@ -208,24 +208,19 @@ public: color: #33E4E4E4; } )"; - key = param.toStdString(); - int value = atoi(params.get(key).c_str()); button_group = new QButtonGroup(this); button_group->setExclusive(true); for (int i = 0; i < button_texts.size(); i++) { QPushButton *button = new QPushButton(button_texts[i], this); button->setCheckable(true); - button->setChecked(i == value); button->setStyleSheet(style); button->setMinimumWidth(minimum_button_width); hlayout->addWidget(button); button_group->addButton(button, i); } - QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), [=](int id) { - params.put(key, std::to_string(id)); - }); + QObject::connect(button_group, QOverload::of(&QButtonGroup::buttonClicked), this, &MultiButtonControl::buttonClicked); } void setEnabled(bool enable) { @@ -238,6 +233,31 @@ public: button_group->button(id)->setChecked(true); } +signals: + void buttonClicked(int id); + +protected: + QButtonGroup *button_group; +}; + +class ButtonParamControl : public MultiButtonControl { + Q_OBJECT +public: + ButtonParamControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, + const std::vector &button_texts, const int minimum_button_width = 225) : MultiButtonControl(title, desc, icon, + button_texts, minimum_button_width) { + key = param.toStdString(); + int value = atoi(params.get(key).c_str()); + + if (value > 0 && value < button_group->buttons().size()) { + button_group->button(value)->setChecked(true); + } + + QObject::connect(this, QOverload::of(&MultiButtonControl::buttonClicked), [=](int id) { + params.put(key, std::to_string(id)); + }); + } + void refresh() { int value = atoi(params.get(key).c_str()); button_group->button(value)->setChecked(true); @@ -250,7 +270,6 @@ public: private: std::string key; Params params; - QButtonGroup *button_group; }; class ListWidget : public QWidget { From 472feefcfdf20a8f4823e28baf596868fd70391b Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Wed, 21 May 2025 01:52:14 +0100 Subject: [PATCH 302/477] Revert "ui: rewrite installer using raylib, remove qt (#33756)" This reverts commit 9cd939d35497bc6d96607d4f5de3441501facd6f. --- selfdrive/ui/SConscript | 26 ++-- selfdrive/ui/installer/installer.cc | 182 ++++++++++++++----------- selfdrive/ui/installer/installer.h | 28 ++++ selfdrive/ui/installer/inter-ascii.ttf | 3 - 4 files changed, 142 insertions(+), 97 deletions(-) create mode 100644 selfdrive/ui/installer/installer.h delete mode 100644 selfdrive/ui/installer/inter-ascii.ttf diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 7199399d41..e63359da05 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,5 +1,6 @@ +import os import json -Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -75,15 +76,8 @@ if GetOption('extras'): if arch != "Darwin": # build installers - raylib_env = env.Clone() - raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] - raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') - - raylib_libs = common + ["raylib"] - if arch == "larch64": - raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"] - else: - raylib_libs += ["GL"] + senv = qt_env.Clone() + senv['LINKFLAGS'].append('-Wl,-strip-debug') release = "release3" installers = [ @@ -93,19 +87,17 @@ if GetOption('extras'): ("openpilot_internal", "nightly-dev"), ] - cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", - "ld -r -b binary -o $TARGET $SOURCE") - inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", - "ld -r -b binary -o $TARGET $SOURCE") + cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh", + "ld -r -b binary -o $TARGET $SOURCE") for name, branch in installers: d = {'BRANCH': f"'\"{branch}\"'"} if "internal" in name: d['INTERNAL'] = "1" - obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) - f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) + obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) + f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs) # keep installers small - assert f[0].get_size() < 1300*1e3, f[0].get_size() + assert f[0].get_size() < 370*1e3 # build watch3 if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'): diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 7326e089ab..84486e61be 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -1,15 +1,19 @@ -#include -#include +#include + +#include #include #include +#include + +#include +#include +#include +#include -#include "common/swaglog.h" #include "common/util.h" -#include "third_party/raylib/include/raylib.h" - -int freshClone(); -int cachedFetch(const std::string &cache); -int executeGitCommand(const std::string &cmd); +#include "selfdrive/ui/installer/installer.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/qt_window.h" std::string get_str(std::string const s) { std::string::size_type pos = s.find('?'); @@ -24,108 +28,136 @@ const std::string BRANCH_STR = get_str(BRANCH "? #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" -const std::string CACHE_PATH = "/data/openpilot.cache"; +const QString CACHE_PATH = "/data/openpilot.cache"; #define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); -extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); -extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end"); - -Font font; void run(const char* cmd) { int err = std::system(cmd); assert(err == 0); } -void renderProgress(int progress) { - BeginDrawing(); - ClearBackground(BLACK); - DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE); - Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72}; - DrawRectangleRec(bar, (Color){41, 41, 41, 255}); - progress = std::clamp(progress, 0, 100); - bar.width *= progress / 100.0f; - DrawRectangleRec(bar, (Color){70, 91, 234, 255}); - DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE); - EndDrawing(); +Installer::Installer(QWidget *parent) : QWidget(parent) { + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(150, 290, 150, 150); + layout->setSpacing(0); + + QLabel *title = new QLabel(tr("Installing...")); + title->setStyleSheet("font-size: 90px; font-weight: 600;"); + layout->addWidget(title, 0, Qt::AlignTop); + + layout->addSpacing(170); + + bar = new QProgressBar(); + bar->setRange(0, 100); + bar->setTextVisible(false); + bar->setFixedHeight(72); + layout->addWidget(bar, 0, Qt::AlignTop); + + layout->addSpacing(30); + + val = new QLabel("0%"); + val->setStyleSheet("font-size: 70px; font-weight: 300;"); + layout->addWidget(val, 0, Qt::AlignTop); + + layout->addStretch(); + + QObject::connect(&proc, QOverload::of(&QProcess::finished), this, &Installer::cloneFinished); + QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress); + + QTimer::singleShot(100, this, &Installer::doInstall); + + setStyleSheet(R"( + * { + font-family: Inter; + color: white; + background-color: black; + } + QProgressBar { + border: none; + background-color: #292929; + } + QProgressBar::chunk { + background-color: #364DEF; + } + )"); } -int doInstall() { +void Installer::updateProgress(int percent) { + bar->setValue(percent); + val->setText(QString("%1%").arg(percent)); + update(); +} + +void Installer::doInstall() { // wait for valid time while (!util::system_time_valid()) { - util::sleep_for(500); - LOGD("Waiting for valid time"); + usleep(500 * 1000); + qDebug() << "Waiting for valid time"; } // cleanup previous install attempts run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); // do the install - if (util::file_exists(CACHE_PATH)) { - return cachedFetch(CACHE_PATH); + if (QDir(CACHE_PATH).exists()) { + cachedFetch(CACHE_PATH); } else { - return freshClone(); + freshClone(); } } -int freshClone() { - LOGD("Doing fresh clone"); - std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", - GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); - return executeGitCommand(cmd); +void Installer::freshClone() { + qDebug() << "Doing fresh clone"; + proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(), + "--depth=1", "--recurse-submodules", TMP_INSTALL_PATH}); } -int cachedFetch(const std::string &cache) { - LOGD("Fetching with cache: %s", cache.c_str()); +void Installer::cachedFetch(const QString &cache) { + qDebug() << "Fetching with cache: " << cache; - run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); - run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); + run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str()); + int err = chdir(TMP_INSTALL_PATH); + assert(err == 0); + run(("git remote set-branches --add origin " + BRANCH_STR).c_str()); - renderProgress(10); + updateProgress(10); - return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); + proc.setWorkingDirectory(TMP_INSTALL_PATH); + proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()}); } -int executeGitCommand(const std::string &cmd) { - static const std::array stages = { +void Installer::readProgress() { + const QVector> stages = { // prefix, weight in percentage - std::pair{"Receiving objects: ", 91}, - std::pair{"Resolving deltas: ", 2}, - std::pair{"Updating files: ", 7}, + {"Receiving objects: ", 91}, + {"Resolving deltas: ", 2}, + {"Updating files: ", 7}, }; - FILE *pipe = popen(cmd.c_str(), "r"); - if (!pipe) return -1; + auto line = QString(proc.readAllStandardError()); - char buffer[512]; - while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { - std::string line(buffer); - int base = 0; - for (const auto &[text, weight] : stages) { - if (line.find(text) != std::string::npos) { - size_t percentPos = line.find("%"); - if (percentPos != std::string::npos && percentPos >= 3) { - int percent = std::stoi(line.substr(percentPos - 3, 3)); - int progress = base + int(percent / 100. * weight); - renderProgress(progress); - } - break; - } - base += weight; + int base = 0; + for (const QPair kv : stages) { + if (line.startsWith(kv.first)) { + auto perc = line.split(kv.first)[1].split("%")[0]; + int p = base + int(perc.toFloat() / 100. * kv.second); + updateProgress(p); + break; } + base += kv.second; } - return pclose(pipe); } -void cloneFinished(int exitCode) { - LOGD("git finished with %d", exitCode); +void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "git finished with " << exitCode; assert(exitCode == 0); - renderProgress(100); + updateProgress(100); // ensure correct branch is checked out int err = chdir(TMP_INSTALL_PATH); @@ -171,17 +203,13 @@ void cloneFinished(int exitCode) { run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over - util::sleep_for(60 * 1000); + QTimer::singleShot(60 * 1000, &QCoreApplication::quit); } int main(int argc, char *argv[]) { - InitWindow(2160, 1080, "Installer"); - font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); - SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); - renderProgress(0); - int result = doInstall(); - cloneFinished(result); - CloseWindow(); - UnloadFont(font); - return 0; + initApp(argc, argv); + QApplication a(argc, argv); + Installer installer; + setMainWindow(&installer); + return a.exec(); } diff --git a/selfdrive/ui/installer/installer.h b/selfdrive/ui/installer/installer.h new file mode 100644 index 0000000000..de3af0ff39 --- /dev/null +++ b/selfdrive/ui/installer/installer.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +class Installer : public QWidget { + Q_OBJECT + +public: + explicit Installer(QWidget *parent = 0); + +private slots: + void updateProgress(int percent); + + void readProgress(); + void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus); + +private: + QLabel *val; + QProgressBar *bar; + QProcess proc; + + void doInstall(); + void freshClone(); + void cachedFetch(const QString &cache); +}; diff --git a/selfdrive/ui/installer/inter-ascii.ttf b/selfdrive/ui/installer/inter-ascii.ttf deleted file mode 100644 index 5d480c515a..0000000000 --- a/selfdrive/ui/installer/inter-ascii.ttf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ef26a4099ef867f3493389379d882381a2491cdbfa41a086be8899a2154dcb3 -size 26160 From 350a235303029020a365a4819888a3ba25635223 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Tue, 20 May 2025 19:41:58 -0700 Subject: [PATCH 303/477] modeld: more USB GPU fixes (#35306) * fixups * builds --- selfdrive/modeld/SConscript | 45 +++++++++++++------ selfdrive/modeld/modeld.py | 4 +- .../modeld/models/big_driving_policy.onnx | 1 + .../modeld/models/big_driving_vision.onnx | 1 + tinygrad_repo | 2 +- 5 files changed, 37 insertions(+), 16 deletions(-) create mode 120000 selfdrive/modeld/models/big_driving_policy.onnx create mode 120000 selfdrive/modeld/models/big_driving_vision.onnx diff --git a/selfdrive/modeld/SConscript b/selfdrive/modeld/SConscript index a6be5c15da..10f3876190 100644 --- a/selfdrive/modeld/SConscript +++ b/selfdrive/modeld/SConscript @@ -38,18 +38,35 @@ for model_name in ['driving_vision', 'driving_policy']: cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx' lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd) -# Compile tinygrad model -pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"' -for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: - if "USBGPU" in os.environ and not model_name.startswith("dmon"): - device_string = "AMD=1 AMD_LLVM=1 NOLOCALS=0 IMAGE=0" - elif arch == 'larch64': - device_string = 'QCOM=1' - elif arch == 'Darwin': - device_string = 'CPU=1 IMAGE=0 JIT=2' - else: - device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0 JIT=2' - +def tg_compile(flags, model_name): + pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"' fn = File(f"models/{model_name}").abspath - cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' - lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd) + return lenv.Command( + fn + "_tinygrad.pkl", + [fn + ".onnx"] + tinygrad_files, + f'{pythonpath_string} {flags} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl' + ) + +# Compile small models +for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']: + flags = { + 'larch64': 'QCOM=1', + 'Darwin': 'CPU=1 IMAGE=0 JIT=2', + }.get(arch, 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0 JIT=2') + tg_compile(flags, model_name) + +# Compile BIG model if USB GPU is available +import subprocess +from tinygrad import Device + +# because tg doesn't support multi-process +devs = subprocess.check_output('python3 -c "from tinygrad import Device; print(list(Device.get_available_devices()))"', shell=True) +if b"AMD" in devs: + del Device + print("USB GPU detected... building") + flags = "AMD=1 AMD_IFACE=USB AMD_LLVM=1 NOLOCALS=0 IMAGE=0" + bp = tg_compile(flags, "big_driving_policy") + bv = tg_compile(flags, "big_driving_vision") + lenv.SideEffect('lock', [bp, bv]) # tg doesn't support multi-process so build serially +else: + print("USB GPU not detected... skipping") diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index cdb16a6f63..1e3d1782e6 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -4,6 +4,7 @@ from openpilot.system.hardware import TICI USBGPU = "USBGPU" in os.environ if USBGPU: os.environ['AMD'] = '1' + os.environ['AMD_IFACE'] = 'USB' elif TICI: from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address os.environ['QCOM'] = '1' @@ -196,11 +197,12 @@ def main(demo=False): # also need to move the aux USB interrupts for good timings config_realtime_process(7, 54) + st = time.monotonic() cloudlog.warning("setting up CL context") cl_context = CLContext() cloudlog.warning("CL context ready; loading model") model = ModelState(cl_context) - cloudlog.warning("models loaded, modeld starting") + cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting") # visionipc clients while True: diff --git a/selfdrive/modeld/models/big_driving_policy.onnx b/selfdrive/modeld/models/big_driving_policy.onnx new file mode 120000 index 0000000000..e1b653a14a --- /dev/null +++ b/selfdrive/modeld/models/big_driving_policy.onnx @@ -0,0 +1 @@ +driving_policy.onnx \ No newline at end of file diff --git a/selfdrive/modeld/models/big_driving_vision.onnx b/selfdrive/modeld/models/big_driving_vision.onnx new file mode 120000 index 0000000000..28ee71dd74 --- /dev/null +++ b/selfdrive/modeld/models/big_driving_vision.onnx @@ -0,0 +1 @@ +driving_vision.onnx \ No newline at end of file diff --git a/tinygrad_repo b/tinygrad_repo index 2895198c36..519dec6677 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 2895198c368e226e2176aae37d5e545691ce85cb +Subproject commit 519dec6677f98718ee4f2d07be1936eb91dde73b From 623ef9f59288e1178b5b4145800e21d4d9f4b1d4 Mon Sep 17 00:00:00 2001 From: Nayan Date: Wed, 21 May 2025 01:14:08 -0400 Subject: [PATCH 304/477] ui: add expandable row widget (#916) * expandable_row widget * make MinMaxValue protected to allow inheritence * add function to set width * no more layout warnings * format --------- Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/SConscript | 1 + selfdrive/ui/sunnypilot/qt/widgets/controls.h | 13 +++-- .../sunnypilot/qt/widgets/expandable_row.cc | 31 ++++++++++++ .../ui/sunnypilot/qt/widgets/expandable_row.h | 50 +++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/expandable_row.cc create mode 100644 selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 379adfcede..d189751843 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -3,6 +3,7 @@ widgets_src = [ "sunnypilot/qt/widgets/toggle.cc", "sunnypilot/qt/widgets/controls.cc", "sunnypilot/qt/widgets/drive_stats.cc", + "sunnypilot/qt/widgets/expandable_row.cc", "sunnypilot/qt/widgets/prime.cc", "sunnypilot/qt/widgets/scrollview.cc", "sunnypilot/qt/network/networking.cc", diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index b6efd871d1..b5411f3bf1 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -441,15 +441,16 @@ public: class OptionControlSP : public AbstractControlSP_SELECTOR { Q_OBJECT -private: - bool is_inline_layout; - QHBoxLayout *optionSelectorLayout = is_inline_layout ? new QHBoxLayout() : hlayout; - +protected: struct MinMaxValue { int min_value; int max_value; }; +private: + bool is_inline_layout; + QHBoxLayout *optionSelectorLayout = is_inline_layout ? new QHBoxLayout() : hlayout; + int getParamValue() { const auto param_value = QString::fromStdString(params.get(key)); const auto result = valueMap != nullptr ? valueMap->key(param_value) : param_value; @@ -551,6 +552,10 @@ public: request_update = _update; } + void setFixedWidth(int width) { + label.setFixedWidth(width); + } + inline void setLabel(const QString &text) { label.setText(text); } void setEnabled(bool enabled) { diff --git a/selfdrive/ui/sunnypilot/qt/widgets/expandable_row.cc b/selfdrive/ui/sunnypilot/qt/widgets/expandable_row.cc new file mode 100644 index 0000000000..b918938d11 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/expandable_row.cc @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h" + +ExpandableToggleRow::ExpandableToggleRow(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent) + : ToggleControlSP(title, desc, icon, false, parent) { + + key = param.toStdString(); + QObject::connect(this, &ExpandableToggleRow::toggleFlipped, this, &ExpandableToggleRow::toggleClicked); + + collapsibleWidget = new QFrame(this); + collapsibleWidget->setContentsMargins(0, 0, 0, 0); + collapsibleWidget->setVisible(false); + QVBoxLayout *collapsible_layout = new QVBoxLayout(); + collapsibleWidget->setLayout(collapsible_layout); + + list = new ListWidgetSP(this, false); + + main_layout->addWidget(collapsibleWidget); + collapsible_layout->addWidget(list); +} + +void ExpandableToggleRow::toggleClicked(bool state) { + params.putBool(key, state); + collapsibleWidget->setVisible(state); +} diff --git a/selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h b/selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h new file mode 100644 index 0000000000..4bfcb9ef55 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class ExpandableToggleRow : public ToggleControlSP { + Q_OBJECT + +public: + ExpandableToggleRow(const QString ¶m, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr); + + void addItem(QWidget *widget) { + list->addItem(widget); + } + + ListWidgetSP *innerList() { + return list; + } + + void refresh() { + bool state = params.getBool(key); + if (state != toggle.on) { + toggle.togglePosition(); + } + collapsibleWidget->setVisible(state); + } + + bool isToggled() { + return params.getBool(key); + } + + void showEvent(QShowEvent *event) override { + refresh(); + } + +private: + void toggleClicked(bool state); + + std::string key; + Params params; + + ListWidgetSP *list; + QFrame *collapsibleWidget = nullptr; +}; From 7ea8277e399e57937379323650d0d7888f95d80d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 21 May 2025 19:28:04 +0800 Subject: [PATCH 305/477] system/ui: improve button press behavior (#35309) improve button press behavior --- system/ui/lib/button.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index 4870e333df..9ca82c9732 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -68,19 +68,22 @@ def gui_button( # Set background color based on button type bg_color = BUTTON_BACKGROUND_COLORS[button_style] mouse_over = is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect) + is_pressed = button_id in _pressed_buttons if mouse_over: if rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - # Record that this button was pressed + # Only this button enters pressed state _pressed_buttons.add(button_id) + is_pressed = True + + # Use pressed color when mouse is down over this button + if is_pressed and rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] - elif rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): - bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] - elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): - # Check if this button was previously pressed - if button_id in _pressed_buttons: - result = 1 - _pressed_buttons.remove(button_id) + + # Handle button click + if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and is_pressed: + result = 1 + _pressed_buttons.remove(button_id) # Clean up pressed state if mouse is released anywhere if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and button_id in _pressed_buttons: From 4c9655b5e4e87e0abc35bd442e3075af61fc1c19 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 22 May 2025 00:44:49 +0800 Subject: [PATCH 306/477] system/ui: add text scrolling support to InputBox for long text (#35310) * add text scrolling support to InputBox for long text * add 2 pixels buffer to the scissor region --- system/ui/lib/inputbox.py | 66 +++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 78ee03bdbd..b164fa4f2a 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -14,6 +14,8 @@ class InputBox: self._key_press_time = 0 self._repeat_delay = 30 self._repeat_rate = 4 + self._text_offset = 0 + self._visible_width = 0 @property def text(self): @@ -23,6 +25,7 @@ class InputBox: def text(self, value): self._input_text = value[: self._max_text_size] self._cursor_position = len(self._input_text) + self._update_text_offset() def set_password_mode(self, password_mode): self._password_mode = password_mode @@ -30,6 +33,7 @@ class InputBox: def clear(self): self._input_text = '' self._cursor_position = 0 + self._text_offset = 0 def set_cursor_position(self, position): """Set the cursor position and reset the blink counter.""" @@ -37,6 +41,29 @@ class InputBox: self._cursor_position = position self._blink_counter = 0 self._show_cursor = True + self._update_text_offset() + + def _update_text_offset(self): + """Ensure the cursor is visible by adjusting text offset.""" + if self._visible_width == 0: + return + + font = gui_app.font() + display_text = "*" * len(self._input_text) if self._password_mode else self._input_text + padding = 10 + + if self._cursor_position > 0: + cursor_x = rl.measure_text_ex(font, display_text[: self._cursor_position], self._font_size, 0).x + else: + cursor_x = 0 + + visible_width = self._visible_width - (padding * 2) + + # Adjust offset if cursor would be outside visible area + if cursor_x < self._text_offset: + self._text_offset = max(0, cursor_x - padding) + elif cursor_x > self._text_offset + visible_width: + self._text_offset = cursor_x - visible_width + padding def add_char_at_cursor(self, char): """Add a character at the current cursor position.""" @@ -63,6 +90,10 @@ class InputBox: return False def render(self, rect, color=rl.BLACK, border_color=rl.DARKGRAY, text_color=rl.WHITE, font_size=80): + # Store dimensions for text offset calculations + self._visible_width = rect.width + self._font_size = font_size + # Handle mouse input self._handle_mouse_input(rect, font_size) @@ -82,10 +113,14 @@ class InputBox: font = gui_app.font() display_text = "*" * len(self._input_text) if self._password_mode else self._input_text padding = 10 + + # Clip text within input box bounds + buffer = 2 + rl.begin_scissor_mode(int(rect.x + padding - buffer), int(rect.y), int(rect.width - padding * 2 + buffer * 2), int(rect.height)) rl.draw_text_ex( font, display_text, - rl.Vector2(int(rect.x + padding), int(rect.y + rect.height / 2 - font_size / 2)), + rl.Vector2(int(rect.x + padding - self._text_offset), int(rect.y + rect.height / 2 - font_size / 2)), font_size, 0, text_color, @@ -97,9 +132,14 @@ class InputBox: if len(display_text) > 0 and self._cursor_position > 0: cursor_x += rl.measure_text_ex(font, display_text[: self._cursor_position], font_size, 0).x + # Apply text offset to cursor position + cursor_x -= self._text_offset + cursor_height = font_size + 4 cursor_y = rect.y + rect.height / 2 - cursor_height / 2 - rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.LIGHTGRAY) + rl.draw_line(int(cursor_x), int(cursor_y), int(cursor_x), int(cursor_y + cursor_height), rl.WHITE) + + rl.end_scissor_mode() def _handle_mouse_input(self, rect, font_size): """Handle mouse clicks to position cursor.""" @@ -107,14 +147,22 @@ class InputBox: if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON) and rl.check_collision_point_rec(mouse_pos, rect): # Calculate cursor position from click if len(self._input_text) > 0: - text_width = rl.measure_text_ex(gui_app.font(), self._input_text, font_size, 0).x - text_pos_x = rect.x + 10 + font = gui_app.font() + display_text = "*" * len(self._input_text) if self._password_mode else self._input_text - if mouse_pos.x - text_pos_x > text_width: - self.set_cursor_position(len(self._input_text)) - else: - click_ratio = (mouse_pos.x - text_pos_x) / text_width - self.set_cursor_position(int(len(self._input_text) * click_ratio)) + # Find the closest character position to the click + relative_x = mouse_pos.x - (rect.x + 10) + self._text_offset + best_pos = 0 + min_distance = float('inf') + + for i in range(len(self._input_text) + 1): + char_width = rl.measure_text_ex(font, display_text[:i], font_size, 0).x + distance = abs(relative_x - char_width) + if distance < min_distance: + min_distance = distance + best_pos = i + + self.set_cursor_position(best_pos) else: self.set_cursor_position(0) From 68fa8c56ab73169612312914147270baace2664c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 22 May 2025 02:17:03 +0800 Subject: [PATCH 307/477] =?UTF-8?q?system/ui:=20use=20=E2=80=A2=20for=20pa?= =?UTF-8?q?ssword=20masking=20in=20InputBox=20(#35313)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use • for password masking --- system/ui/lib/inputbox.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index b164fa4f2a..87e0e92d51 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -2,6 +2,9 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app +PASSWORD_MASK_CHAR = "•" + + class InputBox: def __init__(self, max_text_size=255, password_mode=False): self._max_text_size = max_text_size @@ -49,7 +52,7 @@ class InputBox: return font = gui_app.font() - display_text = "*" * len(self._input_text) if self._password_mode else self._input_text + display_text = PASSWORD_MASK_CHAR * len(self._input_text) if self._password_mode else self._input_text padding = 10 if self._cursor_position > 0: @@ -111,7 +114,7 @@ class InputBox: # Display text font = gui_app.font() - display_text = "*" * len(self._input_text) if self._password_mode else self._input_text + display_text = PASSWORD_MASK_CHAR * len(self._input_text) if self._password_mode else self._input_text padding = 10 # Clip text within input box bounds @@ -148,7 +151,7 @@ class InputBox: # Calculate cursor position from click if len(self._input_text) > 0: font = gui_app.font() - display_text = "*" * len(self._input_text) if self._password_mode else self._input_text + display_text = PASSWORD_MASK_CHAR * len(self._input_text) if self._password_mode else self._input_text # Find the closest character position to the click relative_x = mouse_pos.x - (rect.x + 10) + self._text_offset From 19ad50a185bb80bbe2de4f6154cc0b93bafee506 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 21 May 2025 11:33:47 -0700 Subject: [PATCH 308/477] feat(route.py): add metadata, events retrieval (#35261) * feat(route.py): add metadata, events retrieval * whitespace * raise for status --- tools/lib/route.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/tools/lib/route.py b/tools/lib/route.py index ddb8b0486e..0a4700f083 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -1,12 +1,13 @@ import os import re +import requests from functools import cache from urllib.parse import urlparse from collections import defaultdict from itertools import chain from openpilot.tools.lib.auth_config import get_token -from openpilot.tools.lib.api import CommaApi +from openpilot.tools.lib.api import APIError, CommaApi from openpilot.tools.lib.helpers import RE QLOG_FILENAMES = ['qlog', 'qlog.bz2', 'qlog.zst'] @@ -19,6 +20,7 @@ ECAMERA_FILENAMES = ['ecamera.hevc'] class Route: def __init__(self, name, data_dir=None): + self._metadata = None self._name = RouteName(name) self.files = None if data_dir is not None: @@ -27,6 +29,13 @@ class Route: self._segments = self._get_segments_remote() self.max_seg_number = self._segments[-1].name.segment_num + @property + def metadata(self): + if not self._metadata: + api = CommaApi(get_token()) + self._metadata = api.get('v1/route/' + self.name.canonical_name) + return self._metadata + @property def name(self): return self._name @@ -78,6 +87,7 @@ class Route: url if fn in DCAMERA_FILENAMES else segments[segment_name].dcamera_path, url if fn in ECAMERA_FILENAMES else segments[segment_name].ecamera_path, url if fn in QCAMERA_FILENAMES else segments[segment_name].qcamera_path, + self.metadata['url'], ) else: segments[segment_name] = Segment( @@ -88,6 +98,7 @@ class Route: url if fn in DCAMERA_FILENAMES else None, url if fn in ECAMERA_FILENAMES else None, url if fn in QCAMERA_FILENAMES else None, + self.metadata['url'], ) return sorted(segments.values(), key=lambda seg: seg.name.segment_num) @@ -153,7 +164,7 @@ class Route: except StopIteration: qcamera_path = None - segments.append(Segment(segment, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path)) + segments.append(Segment(segment, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path, self.metadata['url'])) if len(segments) == 0: raise ValueError(f'Could not find segments for route {self.name.canonical_name} in data directory {data_dir}') @@ -161,8 +172,10 @@ class Route: class Segment: - def __init__(self, name, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path): + def __init__(self, name, log_path, qlog_path, camera_path, dcamera_path, ecamera_path, qcamera_path, url): + self._events = None self._name = SegmentName(name) + self.url = f'{url}/{self._name.segment_num}' self.log_path = log_path self.qlog_path = qlog_path self.camera_path = camera_path @@ -174,6 +187,17 @@ class Segment: def name(self): return self._name + @property + def events(self): + if not self._events: + try: + resp = requests.get(f'{self.url}/events.json') + resp.raise_for_status() + self._events = resp.json() + except Exception as e: + raise APIError(f'error getting events for segment {self._name}') from e + return self._events + class RouteName: def __init__(self, name_str: str): From b4d6b52edd815364b9d3503aff6dd3ce1005fe45 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Wed, 21 May 2025 11:48:49 -0700 Subject: [PATCH 309/477] Vegan Filet o Fish model (#35240) e30305e9-5fb8-48bd-a278-f714e880aef1/700 --- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 3601bbb5da..5da6d85300 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36 -size 15966721 +oid sha256:abbb1f5a74a6d047ed4b7f376b19451a9443986660f136afd0f8d76fc254a579 +size 15976894 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index aee6d8f1bf..6c8cf592b0 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624 +oid sha256:6dfffac033d7ae8e68aa5763bd9b5dd99ddc748f6a95523c84fc2523eefdec55 size 34882971 From 4423b47b6c11ca8148b18feaed8e24ca194b339f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 22 May 2025 03:44:15 +0800 Subject: [PATCH 310/477] system/ui: add password mask delay to InputBox (#35316) add password mask delay to InputBox --- system/ui/lib/inputbox.py | 29 ++++++++++++++++++++++++++--- system/ui/widgets/keyboard.py | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 87e0e92d51..367e0dc7e4 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -1,8 +1,10 @@ import pyray as rl +import time from openpilot.system.ui.lib.application import gui_app PASSWORD_MASK_CHAR = "•" +PASSWORD_MASK_DELAY = 1.5 # Seconds to show character before masking class InputBox: @@ -19,6 +21,8 @@ class InputBox: self._repeat_rate = 4 self._text_offset = 0 self._visible_width = 0 + self._last_char_time = 0 # Track when last character was added + self._masked_length = 0 # How many characters are currently masked @property def text(self): @@ -52,7 +56,7 @@ class InputBox: return font = gui_app.font() - display_text = PASSWORD_MASK_CHAR * len(self._input_text) if self._password_mode else self._input_text + display_text = self._get_display_text() padding = 10 if self._cursor_position > 0: @@ -73,6 +77,10 @@ class InputBox: if len(self._input_text) < self._max_text_size: self._input_text = self._input_text[: self._cursor_position] + char + self._input_text[self._cursor_position :] self.set_cursor_position(self._cursor_position + 1) + + if self._password_mode: + self._last_char_time = time.time() + return True return False @@ -114,7 +122,7 @@ class InputBox: # Display text font = gui_app.font() - display_text = PASSWORD_MASK_CHAR * len(self._input_text) if self._password_mode else self._input_text + display_text = self._get_display_text() padding = 10 # Clip text within input box bounds @@ -144,6 +152,21 @@ class InputBox: rl.end_scissor_mode() + def _get_display_text(self): + """Get text to display, applying password masking with delay if needed.""" + if not self._password_mode: + return self._input_text + + # Show character at last edited position if within delay window + masked_text = PASSWORD_MASK_CHAR * len(self._input_text) + recent_edit = time.time() - self._last_char_time < PASSWORD_MASK_DELAY + if recent_edit and self._input_text: + last_pos = max(0, self._cursor_position - 1) + if last_pos < len(self._input_text): + return masked_text[:last_pos] + self._input_text[last_pos] + masked_text[last_pos + 1 :] + + return masked_text + def _handle_mouse_input(self, rect, font_size): """Handle mouse clicks to position cursor.""" mouse_pos = rl.get_mouse_position() @@ -151,7 +174,7 @@ class InputBox: # Calculate cursor position from click if len(self._input_text) > 0: font = gui_app.font() - display_text = PASSWORD_MASK_CHAR * len(self._input_text) if self._password_mode else self._input_text + display_text = self._get_display_text() # Find the closest character position to the click relative_x = mouse_pos.x - (rect.x + 10) + self._text_offset diff --git a/system/ui/widgets/keyboard.py b/system/ui/widgets/keyboard.py index 7836b28184..0e22f1b527 100644 --- a/system/ui/widgets/keyboard.py +++ b/system/ui/widgets/keyboard.py @@ -221,7 +221,7 @@ class Keyboard: if __name__ == "__main__": gui_app.init_window("Keyboard") - keyboard = Keyboard(min_text_size=8) + keyboard = Keyboard(min_text_size=8, show_password_toggle=True) for _ in gui_app.render(): result = keyboard.render("Keyboard", "Type here") if result == 1: From 786b46c0b4621638aa72e67321d338a8157c53dc Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Wed, 21 May 2025 12:45:05 -0700 Subject: [PATCH 311/477] feat: `op esim` and esim.py 2 (#35314) * init: lpa interface * handle multiple messages * handle timeouts * delete old LPA, add enable/disable/validation * check if valid * keep old file the same for easier diff * keep * nickname, bug fixes * space * simple * need to test this on slow conn * initial HITL test for eSIM provisioning * cleanup * lint * test flakes if lpac called concurrently * no * cleanup * org * comment * vibe coded uts * Revert "vibe coded uts" This reverts commit 8b4d8f8ade50dbeaf3fa44f1df1aa2d809deca18. * much simpler test * no value * remove no value add comments * only one test flow now * simpler * reorganize * replace impl * brevity * moar * why didnt u rename * moar * check lpac installed * Profile dataclass * shorten * print out profiles * better * plurals * argparse * download/nickname * move to end to show change * just end early if already enabled * --reboot * reconfigure conn * mutations require reboot today * not needed * lint * guard delete * better * print help * spaceg * rename * support at device * choose backend * desc * more * brackets * op esim * Revert "brackets" This reverts commit 124dbc0cbcc07d93f556ca80a6dc47aa118fda5c. * Update Jenkinsfile --- Jenkinsfile | 2 + system/hardware/tici/esim.py | 233 +++++++++++++++--------- system/hardware/tici/tests/test_esim.py | 51 ++++++ tools/op.sh | 7 + 4 files changed, 204 insertions(+), 89 deletions(-) create mode 100644 system/hardware/tici/tests/test_esim.py diff --git a/Jenkinsfile b/Jenkinsfile index b1a0746ea3..a14bf59299 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -268,6 +268,8 @@ node { step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"), step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]), step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"), + // TODO: enable once new AGNOS is available + // step("test esim", "pytest system/hardware/tici/tests/test_esim.py"), step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]), ]) }, diff --git a/system/hardware/tici/esim.py b/system/hardware/tici/esim.py index df76c1a5fd..0a07e77e16 100755 --- a/system/hardware/tici/esim.py +++ b/system/hardware/tici/esim.py @@ -1,115 +1,170 @@ #!/usr/bin/env python3 + +import argparse +import json import os -import math -import time -import binascii -import requests -import serial +import shutil import subprocess +from dataclasses import dataclass +from typing import Literal +@dataclass +class Profile: + iccid: str + nickname: str + enabled: bool + provider: str -def post(url, payload): - print() - print("POST to", url) - r = requests.post( - url, - data=payload, - verify=False, - headers={ - "Content-Type": "application/json", - "X-Admin-Protocol": "gsma/rsp/v2.2.0", - "charset": "utf-8", - "User-Agent": "gsma-rsp-lpad", - }, - ) - print("resp", r) - print("resp text", repr(r.text)) - print() - r.raise_for_status() +class LPAError(RuntimeError): + pass - ret = f"HTTP/1.1 {r.status_code}" - ret += ''.join(f"{k}: {v}" for k, v in r.headers.items() if k != 'Connection') - return ret.encode() + r.content +class LPAProfileNotFoundError(LPAError): + pass class LPA: - def __init__(self): - self.dev = serial.Serial('/dev/ttyUSB2', baudrate=57600, timeout=1, bytesize=8) - self.dev.reset_input_buffer() - self.dev.reset_output_buffer() - assert "OK" in self.at("AT") + def __init__(self, interface: Literal['qmi', 'at'] = 'qmi'): + self.env = os.environ.copy() + self.env['LPAC_APDU'] = interface + self.env['QMI_DEVICE'] = '/dev/cdc-wdm0' + self.env['AT_DEVICE'] = '/dev/ttyUSB2' - def at(self, cmd): - print(f"==> {cmd}") - self.dev.write(cmd.encode() + b'\r\n') + self.timeout_sec = 45 - r = b"" - cnt = 0 - while b"OK" not in r and b"ERROR" not in r and cnt < 20: - r += self.dev.read(8192).strip() - cnt += 1 - r = r.decode() - print(f"<== {repr(r)}") - return r + if shutil.which('lpac') is None: + raise LPAError('lpac not found, must be installed!') - def download_ota(self, qr): - return self.at(f'AT+QESIM="ota","{qr}"') + def list_profiles(self) -> list[Profile]: + msgs = self._invoke('profile', 'list') + self._validate_successful(msgs) + return [Profile( + iccid=p['iccid'], + nickname=p['profileNickname'], + enabled=p['profileState'] == 'enabled', + provider=p['serviceProviderName'] + ) for p in msgs[-1]['payload']['data']] - def download(self, qr): - smdp = qr.split('$')[1] - out = self.at(f'AT+QESIM="download","{qr}"') - for _ in range(5): - line = out.split("+QESIM: ")[1].split("\r\n\r\nOK")[0] + def get_active_profile(self) -> Profile | None: + return next((p for p in self.list_profiles() if p.enabled), None) - parts = [x.strip().strip('"') for x in line.split(',', maxsplit=4)] - print(repr(parts)) - trans, ret, url, payloadlen, payload = parts - assert trans == "trans" and ret == "0" - assert len(payload) == int(payloadlen) + def enable_profile(self, iccid: str) -> None: + self._validate_profile_exists(iccid) + latest = self.get_active_profile() + if latest: + if latest.iccid == iccid: + return + self.disable_profile(latest.iccid) + self._validate_successful(self._invoke('profile', 'enable', iccid)) + self.process_notifications() - r = post(f"https://{smdp}/{url}", payload) - to_send = binascii.hexlify(r).decode() + def disable_profile(self, iccid: str) -> None: + self._validate_profile_exists(iccid) + latest = self.get_active_profile() + if latest is not None and latest.iccid != iccid: + return + self._validate_successful(self._invoke('profile', 'disable', iccid)) + self.process_notifications() - chunk_len = 1400 - for i in range(math.ceil(len(to_send) / chunk_len)): - state = 1 if (i+1)*chunk_len < len(to_send) else 0 - data = to_send[i * chunk_len : (i+1)*chunk_len] - out = self.at(f'AT+QESIM="trans",{len(to_send)},{state},{i},{len(data)},"{data}"') - assert "OK" in out + def delete_profile(self, iccid: str) -> None: + self._validate_profile_exists(iccid) + latest = self.get_active_profile() + if latest is not None and latest.iccid == iccid: + self.disable_profile(iccid) + self._validate_successful(self._invoke('profile', 'delete', iccid)) + self.process_notifications() - if '+QESIM:"download",1' in out: - raise Exception("profile install failed") - elif '+QESIM:"download",0' in out: - print("done, successfully loaded") - break + def download_profile(self, qr: str, nickname: str | None = None) -> None: + msgs = self._invoke('profile', 'download', '-a', qr) + self._validate_successful(msgs) + new_profile = next((m for m in msgs if m['payload']['message'] == 'es8p_meatadata_parse'), None) + if new_profile is None: + raise LPAError('no new profile found') + if nickname: + self.nickname_profile(new_profile['payload']['data']['iccid'], nickname) + self.process_notifications() - def enable(self, iccid): - self.at(f'AT+QESIM="enable","{iccid}"') + def nickname_profile(self, iccid: str, nickname: str) -> None: + self._validate_profile_exists(iccid) + self._validate_successful(self._invoke('profile', 'nickname', iccid, nickname)) - def disable(self, iccid): - self.at(f'AT+QESIM="disable","{iccid}"') + def process_notifications(self) -> None: + """ + Process notifications stored on the eUICC, typically to activate/deactivate the profile with the carrier. + """ + self._validate_successful(self._invoke('notification', 'process', '-a', '-r')) - def delete(self, iccid): - self.at(f'AT+QESIM="delete","{iccid}"') + def _invoke(self, *cmd: str): + proc = subprocess.Popen(['sudo', '-E', 'lpac'] + list(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env) + try: + out, err = proc.communicate(timeout=self.timeout_sec) + except subprocess.TimeoutExpired as e: + proc.kill() + raise LPAError(f"lpac {cmd} timed out after {self.timeout_sec} seconds") from e - def list_profiles(self): - out = self.at('AT+QESIM="list"') - return out.strip().splitlines()[1:] + messages = [] + for line in out.decode().strip().splitlines(): + if line.startswith('{'): + message = json.loads(line) + + # lpac response format validations + assert 'type' in message, 'expected type in message' + assert message['type'] == 'lpa' or message['type'] == 'progress', 'expected lpa or progress message type' + assert 'payload' in message, 'expected payload in message' + assert 'code' in message['payload'], 'expected code in message payload' + assert 'data' in message['payload'], 'expected data in message payload' + + msg_ret_code = message['payload']['code'] + if msg_ret_code != 0: + raise LPAError(f"lpac {' '.join(cmd)} failed with code {msg_ret_code}: <{message['payload']['message']}> {message['payload']['data']}") + + messages.append(message) + + if len(messages) == 0: + raise LPAError(f"lpac {cmd} returned no messages") + + return messages + + def _validate_profile_exists(self, iccid: str) -> None: + if not any(p.iccid == iccid for p in self.list_profiles()): + raise LPAProfileNotFoundError(f'profile {iccid} does not exist') + + def _validate_successful(self, msgs: list[dict]) -> None: + assert msgs[-1]['payload']['message'] == 'success', 'expected success notification' if __name__ == "__main__": - import sys + parser = argparse.ArgumentParser(prog='esim.py', description='manage eSIM profiles on your comma device', epilog='comma.ai') + parser.add_argument('--backend', choices=['qmi', 'at'], default='qmi', help='use the specified backend, defaults to qmi') + parser.add_argument('--enable', metavar='iccid', help='enable profile; will disable current profile') + parser.add_argument('--disable', metavar='iccid', help='disable profile') + parser.add_argument('--delete', metavar='iccid', help='delete profile (warning: this cannot be undone)') + parser.add_argument('--download', nargs=2, metavar=('qr', 'name'), help='download a profile using QR code (format: LPA:1$rsp.truphone.com$QRF-SPEEDTEST)') + parser.add_argument('--nickname', nargs=2, metavar=('iccid', 'name'), help='update the nickname for a profile') + args = parser.parse_args() - if "RESTART" in os.environ: - subprocess.check_call("sudo systemctl stop ModemManager", shell=True) - subprocess.check_call("/usr/comma/lte/lte.sh stop_blocking", shell=True) - subprocess.check_call("/usr/comma/lte/lte.sh start", shell=True) - while not os.path.exists('/dev/ttyUSB2'): - time.sleep(1) - time.sleep(3) + lpa = LPA(interface=args.backend) + if args.enable: + lpa.enable_profile(args.enable) + print('enabled profile, please restart device to apply changes') + elif args.disable: + lpa.disable_profile(args.disable) + print('disabled profile, please restart device to apply changes') + elif args.delete: + confirm = input('are you sure you want to delete this profile? (y/N) ') + if confirm == 'y': + lpa.delete_profile(args.delete) + print('deleted profile, please restart device to apply changes') + else: + print('cancelled') + exit(0) + elif args.download: + lpa.download_profile(args.download[0], args.download[1]) + elif args.nickname: + lpa.nickname_profile(args.nickname[0], args.nickname[1]) + else: + parser.print_help() - lpa = LPA() - print(lpa.list_profiles()) - if len(sys.argv) > 1: - lpa.download(sys.argv[1]) - print(lpa.list_profiles()) + profiles = lpa.list_profiles() + print(f'\n{len(profiles)} profile{"s" if len(profiles) > 1 else ""}:') + for p in profiles: + print(f'- {p.iccid} (nickname: {p.nickname or ""}) (provider: {p.provider}) - {"enabled" if p.enabled else "disabled"}') diff --git a/system/hardware/tici/tests/test_esim.py b/system/hardware/tici/tests/test_esim.py new file mode 100644 index 0000000000..d36bdaa27b --- /dev/null +++ b/system/hardware/tici/tests/test_esim.py @@ -0,0 +1,51 @@ +import pytest + +from openpilot.system.hardware import TICI +from openpilot.system.hardware.tici.esim import LPA, LPAProfileNotFoundError + +# https://euicc-manual.osmocom.org/docs/rsp/known-test-profile +# iccid is always the same for the given activation code +TEST_ACTIVATION_CODE = 'LPA:1$rsp.truphone.com$QRF-BETTERROAMING-PMRDGIR2EARDEIT5' +TEST_ICCID = '8944476500001944011' + +TEST_NICKNAME = 'test_profile' + +def cleanup(): + lpa = LPA() + try: + lpa.delete_profile(TEST_ICCID) + except LPAProfileNotFoundError: + pass + lpa.process_notifications() + +class TestEsim: + + @classmethod + def setup_class(cls): + if not TICI: + pytest.skip() + cleanup() + + @classmethod + def teardown_class(cls): + cleanup() + + def test_provision_enable_disable(self): + lpa = LPA() + current_active = lpa.get_active_profile() + + lpa.download_profile(TEST_ACTIVATION_CODE, TEST_NICKNAME) + assert any(p.iccid == TEST_ICCID and p.nickname == TEST_NICKNAME for p in lpa.list_profiles()) + + lpa.enable_profile(TEST_ICCID) + new_active = lpa.get_active_profile() + assert new_active is not None + assert new_active.iccid == TEST_ICCID + assert new_active.nickname == TEST_NICKNAME + + lpa.disable_profile(TEST_ICCID) + new_active = lpa.get_active_profile() + assert new_active is None + + if current_active: + lpa.enable_profile(current_active.iccid) diff --git a/tools/op.sh b/tools/op.sh index a7ed964812..7c17c949e4 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -293,6 +293,11 @@ function op_check() { unset VERBOSE } +function op_esim() { + op_before_cmd + op_run_command system/hardware/tici/esim.py "$@" +} + function op_build() { CDIR=$(pwd) op_before_cmd @@ -392,6 +397,7 @@ function op_default() { echo -e "${BOLD}${UNDERLINE}Commands [System]:${NC}" echo -e " ${BOLD}auth${NC} Authenticate yourself for API use" echo -e " ${BOLD}check${NC} Check the development environment (git, os, python) to start using openpilot" + echo -e " ${BOLD}esim${NC} Manage eSIM profiles on your comma device" echo -e " ${BOLD}venv${NC} Activate the python virtual environment" echo -e " ${BOLD}setup${NC} Install openpilot dependencies" echo -e " ${BOLD}build${NC} Run the openpilot build system in the current working directory" @@ -448,6 +454,7 @@ function _op() { auth ) shift 1; op_auth "$@" ;; venv ) shift 1; op_venv "$@" ;; check ) shift 1; op_check "$@" ;; + esim ) shift 1; op_esim "$@" ;; setup ) shift 1; op_setup "$@" ;; build ) shift 1; op_build "$@" ;; juggle ) shift 1; op_juggle "$@" ;; From 163332f368e0f91259243c627d52eb70341c4a3f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 21 May 2025 12:50:08 -0700 Subject: [PATCH 312/477] pandad: fix premature USB panda recovery (#35287) Update pandad.py --- selfdrive/pandad/pandad.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index fd6668feba..b33ffb6473 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -87,7 +87,7 @@ def main() -> None: # TODO: remove this in the next AGNOS # wait until USB is up before counting - if time.monotonic() < 25.: + if time.monotonic() < 35.: no_internal_panda_count = 0 # Handle missing internal panda From 9c40c487292956618c58a4e77b4c9d825d5ae91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 22 May 2025 03:55:19 +0200 Subject: [PATCH 313/477] Revert "Enable online lag learning (#34968)" (#35322) * Revert "Enable online lag learning (#34968)" * Update ref commit --- selfdrive/locationd/torqued.py | 7 +++---- selfdrive/modeld/modeld.py | 4 ++-- selfdrive/test/process_replay/process_replay.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 5ed86e457f..fcaa19df73 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -52,7 +52,7 @@ class TorqueBuckets(PointBuckets): class TorqueEstimator(ParameterEstimator): def __init__(self, CP, decimated=False, track_all_points=False): self.hist_len = int(HISTORY / DT_MDL) - self.lag = 0.0 + self.lag = CP.steerActuatorDelay + .2 # from controlsd self.track_all_points = track_all_points # for offline analysis, without max lateral accel or max steer torque filters if decimated: self.min_bucket_points = MIN_BUCKET_POINTS / 10 @@ -175,8 +175,7 @@ class TorqueEstimator(ParameterEstimator): self.raw_points["steer_override"].append(msg.steeringPressed) elif which == "liveCalibration": self.calibrator.feed_live_calib(msg) - elif which == "liveDelay": - self.lag = msg.lateralDelay + # calculate lateral accel from past steering torque elif which == "livePose": if len(self.raw_points['steer_torque']) == self.hist_len: @@ -242,7 +241,7 @@ def main(demo=False): config_realtime_process([0, 1, 2, 3], 5) pm = messaging.PubMaster(['liveTorqueParameters']) - sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose') + sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose'], poll='livePose') params = Params() estimator = TorqueEstimator(messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 1e3d1782e6..96520a4b15 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -229,7 +229,7 @@ def main(demo=False): # messaging pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) - sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) + sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"]) publish_state = PublishState() params = Params() @@ -256,6 +256,7 @@ def main(demo=False): # TODO this needs more thought, use .2s extra for now to estimate other delays # TODO Move smooth seconds to action function + lat_delay = CP.steerActuatorDelay + .2 + LAT_SMOOTH_SECONDS long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -299,7 +300,6 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - lat_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 8de2e60055..d87a330fdd 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -564,7 +564,7 @@ CONFIGS = [ ), ProcessConfig( proc_name="torqued", - pubs=["livePose", "liveCalibration", "liveDelay", "carState", "carControl", "carOutput"], + pubs=["livePose", "liveCalibration", "carState", "carControl", "carOutput"], subs=["liveTorqueParameters"], ignore=["logMonoTime"], init_callback=get_car_params_callback, @@ -573,7 +573,7 @@ CONFIGS = [ ), ProcessConfig( proc_name="modeld", - pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "liveDelay", "driverMonitoringState", "carState", "carControl"], + pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "driverMonitoringState", "carState", "carControl"], subs=["modelV2", "drivingModelData", "cameraOdometry"], ignore=["logMonoTime", "modelV2.frameDropPerc", "modelV2.modelExecutionTime", "drivingModelData.frameDropPerc", "drivingModelData.modelExecutionTime"], should_recv_callback=ModeldCameraSyncRcvCallback(), diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index f916e7b306..eef00985f3 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -8e57f774d0e8302e1bf1834f84720bb1f9211ae3 \ No newline at end of file +a6c19d1dacbebccee6d14313314e68c5140b836c \ No newline at end of file From 3f53133da5a268c6e32f1abd5f6daaf225d9e66e Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 21 May 2025 19:58:18 -0700 Subject: [PATCH 314/477] AGNOS 12.2 (#35317) * agnos 12.2 * non-staging --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 52 ++++++++-------- system/hardware/tici/all-partitions.json | 76 ++++++++++++------------ 3 files changed, 65 insertions(+), 65 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 73a7a89789..6f31fcf776 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.1" + export AGNOS_VERSION="12.2" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index a415851a10..e209613f42 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -1,25 +1,25 @@ [ { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-468f1ad6ab55e198647ff9191f91bd2918db9c0a3e27bae5673b4c5575c1254c.img.xz", - "hash": "468f1ad6ab55e198647ff9191f91bd2918db9c0a3e27bae5673b4c5575c1254c", - "hash_raw": "468f1ad6ab55e198647ff9191f91bd2918db9c0a3e27bae5673b4c5575c1254c", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1.img.xz", + "hash": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", + "hash_raw": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "d35a86e7b8ddd9279b513a6f27da1521aa0f89fb93987ea74d57d0f0bbbbd247" + "ondevice_hash": "003a17ab1be68a696f7efe4c9938e8be511d4aacfc2f3211fc896bdc1681d089" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-92b675dc2862ed15c732d91d9eb307d7e852e349217db8bee8f8829db543686b.img.xz", - "hash": "92b675dc2862ed15c732d91d9eb307d7e852e349217db8bee8f8829db543686b", - "hash_raw": "92b675dc2862ed15c732d91d9eb307d7e852e349217db8bee8f8829db543686b", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535.img.xz", + "hash": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", + "hash_raw": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "623f1568072ee2d687ba8449a3d894c1c83dc4131b2e79eff35696885f70a419" + "ondevice_hash": "2a855dd636cc94718b64bea83a44d0a31741ecaa8f72a63613ff348ec7404091" }, { "name": "abl", @@ -34,50 +34,50 @@ }, { "name": "aop", - "url": "https://commadist.azureedge.net/agnosupdate/aop-f0fcf7611d0890a72984f15a516dd37fa532dfcb70d428a8406838cf74ce23d5.img.xz", - "hash": "f0fcf7611d0890a72984f15a516dd37fa532dfcb70d428a8406838cf74ce23d5", - "hash_raw": "f0fcf7611d0890a72984f15a516dd37fa532dfcb70d428a8406838cf74ce23d5", + "url": "https://commadist.azureedge.net/agnosupdate/aop-21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9.img.xz", + "hash": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", + "hash_raw": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", "size": 184364, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "bf74feca486f650589f6b7c90eab73274e35a68b5e00bfc1de0ed5f5484d4b3d" + "ondevice_hash": "c1be2f4aac5b3af49b904b027faec418d05efd7bd5144eb4fdfcba602bcf2180" }, { "name": "devcfg", - "url": "https://commadist.azureedge.net/agnosupdate/devcfg-225b24ea7b1d2fee7f7d2da21386920ddacac2e33e9e938168436292f4eae180.img.xz", - "hash": "225b24ea7b1d2fee7f7d2da21386920ddacac2e33e9e938168436292f4eae180", - "hash_raw": "225b24ea7b1d2fee7f7d2da21386920ddacac2e33e9e938168436292f4eae180", + "url": "https://commadist.azureedge.net/agnosupdate/devcfg-d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620.img.xz", + "hash": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", + "hash_raw": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", "size": 40336, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "70f682b59ca0fe2f197d1486bd8be7b9b7e560798ad40ddef83b9f0a2f497938" + "ondevice_hash": "17b229668b20305ff8fa3cd5f94716a3aaa1e5bf9d1c24117eff7f2f81ae719f" }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", - "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", - "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "url": "https://commadist.azureedge.net/agnosupdate/boot-eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968.img.xz", + "hash": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", + "hash_raw": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" + "ondevice_hash": "800868bd9d340f1fdf8340924caca374409624658324607621663fc5c7d10d4f" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz", - "hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939", - "hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", + "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img.xz", + "hash": "226794914e9d157b34cc86f1fbe1f2827a9a9919dbe560021b61573a7e5d3101", + "hash_raw": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc", + "ondevice_hash": "236890f04ee21b7eb92a59bc47971bd96cad5a4381ed8935eb4be0cb5a4cf48b", "alt": { - "hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", - "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img", + "hash": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", + "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index be303b7f90..7e39d275cc 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -130,25 +130,25 @@ }, { "name": "xbl", - "url": "https://commadist.azureedge.net/agnosupdate/xbl-468f1ad6ab55e198647ff9191f91bd2918db9c0a3e27bae5673b4c5575c1254c.img.xz", - "hash": "468f1ad6ab55e198647ff9191f91bd2918db9c0a3e27bae5673b4c5575c1254c", - "hash_raw": "468f1ad6ab55e198647ff9191f91bd2918db9c0a3e27bae5673b4c5575c1254c", + "url": "https://commadist.azureedge.net/agnosupdate/xbl-6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1.img.xz", + "hash": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", + "hash_raw": "6710967ca9701f205d7ab19c3a9b0dd2f547e65b3d96048b7c2b03755aafa0f1", "size": 3282256, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "d35a86e7b8ddd9279b513a6f27da1521aa0f89fb93987ea74d57d0f0bbbbd247" + "ondevice_hash": "003a17ab1be68a696f7efe4c9938e8be511d4aacfc2f3211fc896bdc1681d089" }, { "name": "xbl_config", - "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-92b675dc2862ed15c732d91d9eb307d7e852e349217db8bee8f8829db543686b.img.xz", - "hash": "92b675dc2862ed15c732d91d9eb307d7e852e349217db8bee8f8829db543686b", - "hash_raw": "92b675dc2862ed15c732d91d9eb307d7e852e349217db8bee8f8829db543686b", + "url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535.img.xz", + "hash": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", + "hash_raw": "63922cfbfdf4ab87986c4ba8f3a4df5bf28414b3f71a29ec5947336722215535", "size": 98124, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "623f1568072ee2d687ba8449a3d894c1c83dc4131b2e79eff35696885f70a419" + "ondevice_hash": "2a855dd636cc94718b64bea83a44d0a31741ecaa8f72a63613ff348ec7404091" }, { "name": "abl", @@ -163,14 +163,14 @@ }, { "name": "aop", - "url": "https://commadist.azureedge.net/agnosupdate/aop-f0fcf7611d0890a72984f15a516dd37fa532dfcb70d428a8406838cf74ce23d5.img.xz", - "hash": "f0fcf7611d0890a72984f15a516dd37fa532dfcb70d428a8406838cf74ce23d5", - "hash_raw": "f0fcf7611d0890a72984f15a516dd37fa532dfcb70d428a8406838cf74ce23d5", + "url": "https://commadist.azureedge.net/agnosupdate/aop-21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9.img.xz", + "hash": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", + "hash_raw": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9", "size": 184364, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "bf74feca486f650589f6b7c90eab73274e35a68b5e00bfc1de0ed5f5484d4b3d" + "ondevice_hash": "c1be2f4aac5b3af49b904b027faec418d05efd7bd5144eb4fdfcba602bcf2180" }, { "name": "bluetooth", @@ -207,14 +207,14 @@ }, { "name": "devcfg", - "url": "https://commadist.azureedge.net/agnosupdate/devcfg-225b24ea7b1d2fee7f7d2da21386920ddacac2e33e9e938168436292f4eae180.img.xz", - "hash": "225b24ea7b1d2fee7f7d2da21386920ddacac2e33e9e938168436292f4eae180", - "hash_raw": "225b24ea7b1d2fee7f7d2da21386920ddacac2e33e9e938168436292f4eae180", + "url": "https://commadist.azureedge.net/agnosupdate/devcfg-d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620.img.xz", + "hash": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", + "hash_raw": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620", "size": 40336, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "70f682b59ca0fe2f197d1486bd8be7b9b7e560798ad40ddef83b9f0a2f497938" + "ondevice_hash": "17b229668b20305ff8fa3cd5f94716a3aaa1e5bf9d1c24117eff7f2f81ae719f" }, { "name": "devinfo", @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz", - "hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", - "hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425", + "url": "https://commadist.azureedge.net/agnosupdate/boot-eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968.img.xz", + "hash": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", + "hash_raw": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf" + "ondevice_hash": "800868bd9d340f1fdf8340924caca374409624658324607621663fc5c7d10d4f" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz", - "hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939", - "hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", + "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img.xz", + "hash": "226794914e9d157b34cc86f1fbe1f2827a9a9919dbe560021b61573a7e5d3101", + "hash_raw": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc", + "ondevice_hash": "236890f04ee21b7eb92a59bc47971bd96cad5a4381ed8935eb4be0cb5a4cf48b", "alt": { - "hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7", - "url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img", + "hash": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", + "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac.img.xz", - "hash": "b18001a2a87caa070fabf6321f8215ac353d6444564e3f86329b4dccc039ce54", - "hash_raw": "4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f6d24876234f6bea9cc753892eea99ac4b0c8646e93b93d76fc5dddce67347f1.img.xz", + "hash": "2485459e9fbfc0d88a59a017bfa193d97b80afed30d6f59db711e53f62a21c72", + "hash_raw": "f6d24876234f6bea9cc753892eea99ac4b0c8646e93b93d76fc5dddce67347f1", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "15ce16f2349d5b4d5fec6ad1e36222b1ae744ed10b8930bc9af75bd244dccb3c" + "ondevice_hash": "893cfb3e95d6025b9fa6a5c8c3b97bf2abc4ff036c3da846a07536653dbb3a1d" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8.img.xz", - "hash": "12682cf54596ab1bd1c2464c4ca85888e4e06b47af5ff7d0432399e9907e2f64", - "hash_raw": "e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-14f0f02e53ce62a79b337d7b3174dcaffebaa4130264bb5ed9105d942338230b.img.xz", + "hash": "9222aebb280531b7015ee9bab3848dd195567d075b6f37f74d4cd0186685a11f", + "hash_raw": "14f0f02e53ce62a79b337d7b3174dcaffebaa4130264bb5ed9105d942338230b", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "e4df9dea47ff04967d971263d50c17460ef240457e8d814e7c4f409f7493eb8a" + "ondevice_hash": "a5459ec858d39e3d0709a09af430cb644fe640f92e0cd216b138f01401e4e17e" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282.img.xz", - "hash": "fa471703be0f0647617d183312d5209d23407f1628e4ab0934e6ec54b1a6b263", - "hash_raw": "fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-180ad331538f20c00218d41591afed3a25bb0f76fa30b1a665cad102cf6c9f7d.img.xz", + "hash": "7fc09317f005676150bfcced43ebb1aa919d854c2511d547a588a35c1122bfe3", + "hash_raw": "180ad331538f20c00218d41591afed3a25bb0f76fa30b1a665cad102cf6c9f7d", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "0b5b2402c9caa1ed7b832818e66580c974251e735bda91f2f226c41499d5616e" + "ondevice_hash": "1a2f2d14c922bd2895073446460a0bd680711a7b14318a4402631635e6d133b3" } ] \ No newline at end of file From c9f3cd5ad2ce223f3ff9bc623453aafc0327c8cd Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 22 May 2025 11:31:50 +0800 Subject: [PATCH 315/477] system/ui: enhance scroll panel with iPhone-like physics and behavior (#35312) * improve scroll panel for iphone-like experience * add comments * increase demo run time for easier testing --- system/ui/lib/scroll_panel.py | 168 ++++++++++++++++++++++++++++------ system/ui/text.py | 2 +- 2 files changed, 139 insertions(+), 31 deletions(-) diff --git a/system/ui/lib/scroll_panel.py b/system/ui/lib/scroll_panel.py index 7dc2fdb917..43111504bb 100644 --- a/system/ui/lib/scroll_panel.py +++ b/system/ui/lib/scroll_panel.py @@ -1,16 +1,23 @@ import pyray as rl from enum import IntEnum +# Scroll constants for smooth scrolling behavior MOUSE_WHEEL_SCROLL_SPEED = 30 -INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down -MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia -DRAG_THRESHOLD = 5 # Pixels of movement to consider it a drag, not a click +INERTIA_FRICTION = 0.92 # The rate at which the inertia slows down +MIN_VELOCITY = 0.5 # Minimum velocity before stopping the inertia +DRAG_THRESHOLD = 5 # Pixels of movement to consider it a drag, not a click +BOUNCE_FACTOR = 0.2 # Elastic bounce when scrolling past boundaries +BOUNCE_RETURN_SPEED = 0.15 # How quickly it returns from the bounce +MAX_BOUNCE_DISTANCE = 150 # Maximum distance for bounce effect +FLICK_MULTIPLIER = 1.8 # Multiplier for flick gestures +VELOCITY_HISTORY_SIZE = 5 # Track velocity over multiple frames for smoother motion class ScrollState(IntEnum): IDLE = 0 DRAGGING_CONTENT = 1 DRAGGING_SCROLLBAR = 2 + BOUNCING = 3 class GuiScrollPanel: @@ -22,14 +29,33 @@ class GuiScrollPanel: self._view = rl.Rectangle(0, 0, 0, 0) self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar self._velocity_y = 0.0 # Velocity for inertia - self._is_dragging = False + self._is_dragging: bool = False + self._bounce_offset: float = 0.0 + self._last_frame_time = rl.get_time() + self._velocity_history: list[float] = [] + self._last_drag_time: float = 0.0 + self._content_rect: rl.Rectangle | None = None + self._bounds_rect: rl.Rectangle | None = None def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2: - mouse_pos = rl.get_mouse_position() + # Store rectangles for reference + self._content_rect = content + self._bounds_rect = bounds - # Handle dragging logic + # Calculate time delta + current_time = rl.get_time() + delta_time = current_time - self._last_frame_time + self._last_frame_time = current_time + + # Prevent large jumps + delta_time = min(delta_time, 0.05) + + mouse_pos = rl.get_mouse_position() + max_scroll_y = max(content.height - bounds.height, 0) + + # Start dragging on mouse press if rl.check_collision_point_rec(mouse_pos, bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): - if self._scroll_state == ScrollState.IDLE: + if self._scroll_state == ScrollState.IDLE or self._scroll_state == ScrollState.BOUNCING: self._scroll_state = ScrollState.DRAGGING_CONTENT if self._show_vertical_scroll_bar: scrollbar_width = rl.gui_get_style(rl.GuiControl.LISTVIEW, rl.GuiListViewProperty.SCROLLBAR_WIDTH) @@ -38,51 +64,133 @@ class GuiScrollPanel: self._scroll_state = ScrollState.DRAGGING_SCROLLBAR self._last_mouse_y = mouse_pos.y - self._start_mouse_y = mouse_pos.y # Record starting position - self._velocity_y = 0.0 # Reset velocity when drag starts - self._is_dragging = False # Reset dragging flag + self._start_mouse_y = mouse_pos.y + self._last_drag_time = current_time + self._velocity_history = [] + self._velocity_y = 0.0 + self._bounce_offset = 0.0 + self._is_dragging = False - if self._scroll_state != ScrollState.IDLE: + # Handle active dragging + if self._scroll_state == ScrollState.DRAGGING_CONTENT or self._scroll_state == ScrollState.DRAGGING_SCROLLBAR: if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): delta_y = mouse_pos.y - self._last_mouse_y - # Check if movement exceeds the drag threshold + # Track velocity for inertia + time_since_last_drag = current_time - self._last_drag_time + if time_since_last_drag > 0: + drag_velocity = delta_y / time_since_last_drag / 60.0 + self._velocity_history.append(drag_velocity) + + if len(self._velocity_history) > VELOCITY_HISTORY_SIZE: + self._velocity_history.pop(0) + + self._last_drag_time = current_time + + # Detect actual dragging total_drag = abs(mouse_pos.y - self._start_mouse_y) if total_drag > DRAG_THRESHOLD: self._is_dragging = True if self._scroll_state == ScrollState.DRAGGING_CONTENT: + # Add resistance at boundaries + if (self._offset.y > 0 and delta_y > 0) or (self._offset.y < -max_scroll_y and delta_y < 0): + delta_y *= BOUNCE_FACTOR + self._offset.y += delta_y elif self._scroll_state == ScrollState.DRAGGING_SCROLLBAR: - delta_y = -delta_y + scroll_ratio = content.height / bounds.height + self._offset.y -= delta_y * scroll_ratio self._last_mouse_y = mouse_pos.y - self._velocity_y = delta_y # Update velocity during drag - elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): - self._scroll_state = ScrollState.IDLE - # Handle mouse wheel scrolling + elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): + # Calculate flick velocity + if self._velocity_history: + total_weight = 0 + weighted_velocity = 0.0 + + for i, v in enumerate(self._velocity_history): + weight = i + 1 + weighted_velocity += v * weight + total_weight += weight + + if total_weight > 0: + avg_velocity = weighted_velocity / total_weight + self._velocity_y = avg_velocity * FLICK_MULTIPLIER + + # Check bounds + if self._offset.y > 0 or self._offset.y < -max_scroll_y: + self._scroll_state = ScrollState.BOUNCING + else: + self._scroll_state = ScrollState.IDLE + + # Handle mouse wheel wheel_move = rl.get_mouse_wheel_move() - if self._show_vertical_scroll_bar: - self._offset.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) - rl.gui_scroll_panel(bounds, rl.ffi.NULL, content, self._offset, self._view) - else: - self._offset.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED + if wheel_move != 0: + self._velocity_y = 0.0 + + if self._show_vertical_scroll_bar: + self._offset.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) + rl.gui_scroll_panel(bounds, rl.ffi.NULL, content, self._offset, self._view) + else: + self._offset.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED + + if self._offset.y > 0 or self._offset.y < -max_scroll_y: + self._scroll_state = ScrollState.BOUNCING # Apply inertia (continue scrolling after mouse release) if self._scroll_state == ScrollState.IDLE: - self._offset.y += self._velocity_y - self._velocity_y *= INERTIA_FRICTION # Slow down velocity over time + if abs(self._velocity_y) > MIN_VELOCITY: + self._offset.y += self._velocity_y + self._velocity_y *= INERTIA_FRICTION - # Stop scrolling when velocity is low - if abs(self._velocity_y) < MIN_VELOCITY: + if self._offset.y > 0 or self._offset.y < -max_scroll_y: + self._scroll_state = ScrollState.BOUNCING + else: self._velocity_y = 0.0 - # Ensure scrolling doesn't go beyond bounds - max_scroll_y = max(content.height - bounds.height, 0) - self._offset.y = max(min(self._offset.y, 0), -max_scroll_y) + # Handle bouncing effect + elif self._scroll_state == ScrollState.BOUNCING: + target_y = 0.0 + if self._offset.y < -max_scroll_y: + target_y = -max_scroll_y + + distance = target_y - self._offset.y + bounce_step = distance * BOUNCE_RETURN_SPEED + self._offset.y += bounce_step + self._velocity_y *= INERTIA_FRICTION * 0.8 + + if abs(distance) < 0.5 and abs(self._velocity_y) < MIN_VELOCITY: + self._offset.y = target_y + self._velocity_y = 0.0 + self._scroll_state = ScrollState.IDLE + + # Limit bounce distance + if self._scroll_state != ScrollState.DRAGGING_CONTENT: + if self._offset.y > MAX_BOUNCE_DISTANCE: + self._offset.y = MAX_BOUNCE_DISTANCE + elif self._offset.y < -(max_scroll_y + MAX_BOUNCE_DISTANCE): + self._offset.y = -(max_scroll_y + MAX_BOUNCE_DISTANCE) return self._offset def is_click_valid(self) -> bool: - return self._scroll_state == ScrollState.IDLE and not self._is_dragging and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) + # Check if this is a click rather than a drag + return ( + self._scroll_state == ScrollState.IDLE + and not self._is_dragging + and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) + ) + + def get_normalized_scroll_position(self) -> float: + """Returns the current scroll position as a value from 0.0 to 1.0""" + if not self._content_rect or not self._bounds_rect: + return 0.0 + + max_scroll_y = max(self._content_rect.height - self._bounds_rect.height, 0) + if max_scroll_y == 0: + return 0.0 + + normalized = -self._offset.y / max_scroll_y + return max(0.0, min(1.0, normalized)) diff --git a/system/ui/text.py b/system/ui/text.py index 33e8167c64..82e64d836f 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -88,4 +88,4 @@ class TextWindow(BaseWindow[TextWindowRenderer]): if __name__ == "__main__": with TextWindow(DEMO_TEXT): - time.sleep(5) + time.sleep(30) From f38a98fc0932abc9c6889528ae558132e2b96fc5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 21 May 2025 20:45:47 -0700 Subject: [PATCH 316/477] Metered Wi-Fi toggle (#35293) * draft * here too * fixes * fix * ugh more fix, wifiManager is craze * more * be optimistic and let refresh happen naturally, the immediate refresh causes some paths/active connect to temporarily be unavailable selfdrive/ui/qt/network/wifi_manager.cc: DBus call error: "Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the re ply timeout expired, or the network connection was broken." selfdrive/ui/qt/network/wifi_manager.cc: DBus call error: "Object path cannot be empty" * nm is slow -- it takes 2s to commit to disk, and dbus errors are raised if you try again while previous is running (this is an ubuntu 24.04 bug) * nice experience * rm * minor * clean up * self-explanatory * rename * rm this too * revert * draft * Revert "draft" This reverts commit 15283d977880fc60b8f9732772256e8337d6ac8e. * Reapply "draft" This reverts commit 8629921b0086ca71b88746d77ec7b8d3af3cd289. * rm colors * trinary, bit more code * choose default when disabled * only if enabling, wait for disable as normal * remove original binary toggle * clean up * collapse * clean up wifimanager * update comment * lite is a word --- pyproject.toml | 2 +- selfdrive/ui/qt/network/networking.cc | 46 ++++++++++++++++++++++--- selfdrive/ui/qt/network/networking.h | 3 +- selfdrive/ui/qt/network/wifi_manager.cc | 39 +++++++++++++++++++++ selfdrive/ui/qt/network/wifi_manager.h | 7 ++++ selfdrive/ui/qt/widgets/controls.h | 4 +++ 6 files changed, 94 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 464b605f86..b9a9fcab55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -169,7 +169,7 @@ testpaths = [ [tool.codespell] quiet-level = 3 # if you've got a short variable name that's getting flagged, add it here -ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl" +ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite" builtin = "clear,rare,informal,code,names,en-GB_to_en-US" skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*" diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 93f7ff3f21..bb914b6449 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -173,14 +173,36 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid }); list->addItem(editApnButton); - // Metered toggle + // Cellular metered toggle (prime lite or none) const bool metered = params.getBool("GsmMetered"); - meteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered connection"), "", metered); - QObject::connect(meteredToggle, &SshToggle::toggleFlipped, [=](bool state) { + cellularMeteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered cellular connection"), "", metered); + QObject::connect(cellularMeteredToggle, &SshToggle::toggleFlipped, [=](bool state) { params.putBool("GsmMetered", state); wifi->updateGsmSettings(params.getBool("GsmRoaming"), QString::fromStdString(params.get("GsmApn")), state); }); - list->addItem(meteredToggle); + list->addItem(cellularMeteredToggle); + + // Wi-Fi metered toggle + std::vector metered_button_texts{tr("default"), tr("metered"), tr("unmetered")}; + wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-FI connection"), "", metered_button_texts); + QObject::connect(wifiMeteredToggle, &MultiButtonControl::buttonClicked, [=](int id) { + wifiMeteredToggle->setEnabled(false); + MeteredType metered = MeteredType::UNKNOWN; + if (id == NM_METERED_YES) { + metered = MeteredType::YES; + } else if (id == NM_METERED_NO) { + metered = MeteredType::NO; + } + auto pending_call = wifi->setCurrentNetworkMetered(metered); + if (pending_call) { + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(*pending_call); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() { + refresh(); + watcher->deleteLater(); + }); + } + }); + list->addItem(wifiMeteredToggle); // Hidden Network hiddenNetworkButton = new ButtonControl(tr("Hidden Network"), tr("CONNECT")); @@ -211,18 +233,32 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid void AdvancedNetworking::setGsmVisible(bool visible) { roamingToggle->setVisible(visible); editApnButton->setVisible(visible); - meteredToggle->setVisible(visible); + cellularMeteredToggle->setVisible(visible); } void AdvancedNetworking::refresh() { ipLabel->setText(wifi->ipv4_address); tetheringToggle->setEnabled(true); + + if (wifi->isTetheringEnabled() || wifi->ipv4_address == "") { + wifiMeteredToggle->setEnabled(false); + wifiMeteredToggle->setCheckedButton(0); + } else if (wifi->ipv4_address != "") { + MeteredType metered = wifi->currentNetworkMetered(); + wifiMeteredToggle->setEnabled(true); + wifiMeteredToggle->setCheckedButton(static_cast(metered)); + } + update(); } void AdvancedNetworking::toggleTethering(bool enabled) { wifi->setTetheringEnabled(enabled); tetheringToggle->setEnabled(false); + if (enabled) { + wifiMeteredToggle->setEnabled(false); + wifiMeteredToggle->setCheckedButton(0); + } } // WifiUI functions diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h index 4fd604039b..0bdf64e459 100644 --- a/selfdrive/ui/qt/network/networking.h +++ b/selfdrive/ui/qt/network/networking.h @@ -65,7 +65,8 @@ private: ToggleControl* roamingToggle; ButtonControl* editApnButton; ButtonControl* hiddenNetworkButton; - ToggleControl* meteredToggle; + ToggleControl* cellularMeteredToggle; + MultiButtonControl* wifiMeteredToggle; WifiManager* wifi = nullptr; Params params; diff --git a/selfdrive/ui/qt/network/wifi_manager.cc b/selfdrive/ui/qt/network/wifi_manager.cc index 3d4bb9e7d1..d4ad8974b0 100644 --- a/selfdrive/ui/qt/network/wifi_manager.cc +++ b/selfdrive/ui/qt/network/wifi_manager.cc @@ -353,6 +353,7 @@ void WifiManager::activateModemConnection(const QDBusObjectPath &path) { } // function matches tici/hardware.py +// FIXME: it can mistakenly show CELL when connected to WIFI NetworkType WifiManager::currentNetworkType() { auto primary_conn = call(NM_DBUS_PATH, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE, "PrimaryConnection"); auto primary_type = call(primary_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); @@ -372,6 +373,44 @@ NetworkType WifiManager::currentNetworkType() { return NetworkType::NONE; } +MeteredType WifiManager::currentNetworkMetered() { + MeteredType metered = MeteredType::UNKNOWN; + for (const auto &active_conn : getActiveConnections()) { + QString type = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); + if (type == "802-11-wireless") { + QDBusObjectPath conn = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Connection"); + if (!conn.path().isEmpty()) { + Connection settings = getConnectionSettings(conn); + int metered_prop = settings.value("connection").value("metered").toInt(); + if (metered_prop == NM_METERED_YES) { + metered = MeteredType::YES; + } else if (metered_prop == NM_METERED_NO) { + metered = MeteredType::NO; + } + } + break; + } + } + return metered; +} + +std::optional WifiManager::setCurrentNetworkMetered(MeteredType metered) { + for (const auto &active_conn : getActiveConnections()) { + QString type = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); + if (type == "802-11-wireless") { + if (!isTetheringEnabled()) { + QDBusObjectPath conn = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Connection"); + if (!conn.path().isEmpty()) { + Connection settings = getConnectionSettings(conn); + settings["connection"]["metered"] = static_cast(metered); + return asyncCall(conn.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Update", QVariant::fromValue(settings)); + } + } + } + } + return std::nullopt; +} + void WifiManager::updateGsmSettings(bool roaming, QString apn, bool metered) { if (!lteConnectionPath.path().isEmpty()) { bool changes = false; diff --git a/selfdrive/ui/qt/network/wifi_manager.h b/selfdrive/ui/qt/network/wifi_manager.h index e5f79c5149..cab932a388 100644 --- a/selfdrive/ui/qt/network/wifi_manager.h +++ b/selfdrive/ui/qt/network/wifi_manager.h @@ -22,6 +22,11 @@ enum class NetworkType { CELL, ETHERNET }; +enum class MeteredType { + UNKNOWN, + YES, + NO +}; typedef QMap Connection; typedef QVector IpConfig; @@ -53,6 +58,8 @@ public: bool isKnownConnection(const QString &ssid); std::optional activateWifiConnection(const QString &ssid); NetworkType currentNetworkType(); + MeteredType currentNetworkMetered(); + std::optional setCurrentNetworkMetered(MeteredType metered); void updateGsmSettings(bool roaming, QString apn, bool metered); void connect(const Network &ssid, const bool is_hidden = false, const QString &password = {}, const QString &username = {}); diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index ca93110e5b..3342de5324 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -204,6 +204,9 @@ public: QPushButton:checked:enabled { background-color: #33Ab4C; } + QPushButton:checked:disabled { + background-color: #9933Ab4C; + } QPushButton:disabled { color: #33E4E4E4; } @@ -214,6 +217,7 @@ public: for (int i = 0; i < button_texts.size(); i++) { QPushButton *button = new QPushButton(button_texts[i], this); button->setCheckable(true); + button->setChecked(i == 0); button->setStyleSheet(style); button->setMinimumWidth(minimum_button_width); hlayout->addWidget(button); From fdfba3f9f7792df5725260023d18f93bd503b094 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 23 May 2025 02:23:50 +0800 Subject: [PATCH 317/477] system/ui: update camera view shader to support rendering on device (#35326) update shader to support rendering on device --- system/ui/widgets/cameraview.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index 841440ae3a..09fe306176 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -2,20 +2,41 @@ import pyray as rl from msgq.visionipc import VisionIpcClient, VisionStreamType from openpilot.system.ui.lib.application import gui_app + +VERTEX_SHADER = """ +#version 300 es +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; +uniform mat4 mvp; +out vec2 fragTexCoord; +out vec4 fragColor; +void main() { + fragTexCoord = vertexTexCoord; + fragColor = vertexColor; + gl_Position = mvp * vec4(vertexPosition, 1.0); +} +""" + FRAME_FRAGMENT_SHADER = """ -#version 330 core -in vec2 fragTexCoord; uniform sampler2D texture0, texture1; out vec4 fragColor; +#version 300 es +precision mediump float; +in vec2 fragTexCoord; +uniform sampler2D texture0; +uniform sampler2D texture1; +out vec4 fragColor; void main() { float y = texture(texture0, fragTexCoord).r; vec2 uv = texture(texture1, fragTexCoord).ra - 0.5; fragColor = vec4(y + 1.402*uv.y, y - 0.344*uv.x - 0.714*uv.y, y + 1.772*uv.x, 1.0); -}""" - +} +""" class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): self.client = VisionIpcClient(name, stream_type, False) - self.shader = rl.load_shader_from_memory(rl.ffi.NULL, FRAME_FRAGMENT_SHADER) + self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER) self.texture_y: rl.Texture | None = None self.texture_uv: rl.Texture | None = None self.frame = None From 9b642ed01664b920bdac0e238720c27e70562b0d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 22 May 2025 17:49:27 -0700 Subject: [PATCH 318/477] bump opendbc (#35323) * bump * update refs --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 0676642f40..afb464bd7a 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0676642f4029b02758405b3052d1ef8ad2a5528d +Subproject commit afb464bd7a3134ab5511d26c0ca9060cb2793ed5 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index eef00985f3..0ca95ec5fa 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -a6c19d1dacbebccee6d14313314e68c5140b836c \ No newline at end of file +280cd05fe44e47474d04f2cf7435fb0000a562e5 \ No newline at end of file From f37fc62ed25a189cf128d6be7be3cefe0832f628 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 22 May 2025 20:54:43 -0400 Subject: [PATCH 319/477] Update Python packages (#942) --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 5db6b93bc5..ac3d40b757 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 5db6b93bc550edf5425d0b22e49c9bf51ec23eab +Subproject commit ac3d40b7570d85ba4f8056ac542a10cc8a82ccea From 312658756dbe16b802a90b2e9e9d8d8dcc868779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Fri, 23 May 2025 03:33:55 +0200 Subject: [PATCH 320/477] lagd: handle ambiguous cases (#35257) * Mechanism to handle ambiguous estimates * Use it * Fix static * Fix * Comment * Comment v2 * Use quantile instead * Fix * Make it better * Make it confidence * Make it a constant * min_confidence as arg * Make the test signal more like the real one * Relax sample requirements * Use constant * Reduce confidence --- selfdrive/locationd/lagd.py | 33 ++++++++++++++++++++------- selfdrive/locationd/test/test_lagd.py | 4 ++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index b4f4e1f663..4e207b4882 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -16,17 +16,20 @@ from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose, fft_next BLOCK_SIZE = 100 BLOCK_NUM = 50 BLOCK_NUM_NEEDED = 5 -MOVING_WINDOW_SEC = 300.0 +MOVING_WINDOW_SEC = 60.0 MIN_OKAY_WINDOW_SEC = 25.0 MIN_RECOVERY_BUFFER_SEC = 2.0 MIN_VEGO = 15.0 -MIN_ABS_YAW_RATE = np.radians(1.0) +MIN_ABS_YAW_RATE = 0.0 MAX_YAW_RATE_SANITY_CHECK = 1.0 MIN_NCC = 0.95 MAX_LAG = 1.0 MAX_LAG_STD = 0.1 MAX_LAT_ACCEL = 2.0 MAX_LAT_ACCEL_DIFF = 0.6 +MIN_CONFIDENCE = 0.7 +CORR_BORDER_OFFSET = 5 +LAG_CANDIDATE_CORR_THRESHOLD = 0.9 def masked_normalized_cross_correlation(expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, n: int): @@ -154,7 +157,7 @@ class LateralLagEstimator: block_count: int = BLOCK_NUM, min_valid_block_count: int = BLOCK_NUM_NEEDED, block_size: int = BLOCK_SIZE, window_sec: float = MOVING_WINDOW_SEC, okay_window_sec: float = MIN_OKAY_WINDOW_SEC, min_recovery_buffer_sec: float = MIN_RECOVERY_BUFFER_SEC, min_vego: float = MIN_VEGO, min_yr: float = MIN_ABS_YAW_RATE, min_ncc: float = MIN_NCC, - max_lat_accel: float = MAX_LAT_ACCEL, max_lat_accel_diff: float = MAX_LAT_ACCEL_DIFF): + max_lat_accel: float = MAX_LAT_ACCEL, max_lat_accel_diff: float = MAX_LAT_ACCEL_DIFF, min_confidence: float = MIN_CONFIDENCE): self.dt = dt self.window_sec = window_sec self.okay_window_sec = okay_window_sec @@ -166,6 +169,7 @@ class LateralLagEstimator: self.min_vego = min_vego self.min_yr = min_yr self.min_ncc = min_ncc + self.min_confidence = min_confidence self.max_lat_accel = max_lat_accel self.max_lat_accel_diff = max_lat_accel_diff @@ -292,14 +296,14 @@ class LateralLagEstimator: new_values_start_idx = next(-i for i, t in enumerate(reversed(times)) if t <= self.last_estimate_t) is_valid = is_valid and not (new_values_start_idx == 0 or not np.any(okay[new_values_start_idx:])) - delay, corr = self.actuator_delay(desired, actual, okay, self.dt, MAX_LAG) - if corr < self.min_ncc or not is_valid: + delay, corr, confidence = self.actuator_delay(desired, actual, okay, self.dt, MAX_LAG) + if corr < self.min_ncc or confidence < self.min_confidence or not is_valid: return self.block_avg.update(delay) self.last_estimate_t = self.t - def actuator_delay(self, expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, dt: float, max_lag: float) -> tuple[float, float]: + def actuator_delay(self, expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, dt: float, max_lag: float) -> tuple[float, float, float]: assert len(expected_sig) == len(actual_sig) max_lag_samples = int(max_lag / dt) padded_size = fft_next_good_size(len(expected_sig) + max_lag_samples) @@ -307,13 +311,26 @@ class LateralLagEstimator: ncc = masked_normalized_cross_correlation(expected_sig, actual_sig, mask, padded_size) # only consider lags from 0 to max_lag - roi_ncc = ncc[len(expected_sig) - 1: len(expected_sig) - 1 + max_lag_samples] + roi = np.s_[len(expected_sig) - 1: len(expected_sig) - 1 + max_lag_samples] + extended_roi = np.s_[roi.start - CORR_BORDER_OFFSET: roi.stop + CORR_BORDER_OFFSET] + roi_ncc = ncc[roi] + extended_roi_ncc = ncc[extended_roi] max_corr_index = np.argmax(roi_ncc) corr = roi_ncc[max_corr_index] lag = parabolic_peak_interp(roi_ncc, max_corr_index) * dt - return lag, corr + # to estimate lag confidence, gather all high-correlation candidates and see how spread they are + # if e.g. 0.8 and 0.4 are both viable, this is an ambiguous case + ncc_thresh = (roi_ncc.max() - roi_ncc.min()) * LAG_CANDIDATE_CORR_THRESHOLD + roi_ncc.min() + good_lag_candidate_mask = extended_roi_ncc >= ncc_thresh + good_lag_candidate_edges = np.diff(good_lag_candidate_mask.astype(int), prepend=0, append=0) + starts, ends = np.where(good_lag_candidate_edges == 1)[0], np.where(good_lag_candidate_edges == -1)[0] - 1 + run_idx = np.searchsorted(starts, max_corr_index + CORR_BORDER_OFFSET, side='right') - 1 + width = ends[run_idx] - starts[run_idx] + 1 + confidence = np.clip(1 - width * dt, 0, 1) + + return lag, corr, confidence def retrieve_initial_lag(params: Params, CP: car.CarParams): diff --git a/selfdrive/locationd/test/test_lagd.py b/selfdrive/locationd/test/test_lagd.py index 13aea60a26..b805f1759d 100644 --- a/selfdrive/locationd/test/test_lagd.py +++ b/selfdrive/locationd/test/test_lagd.py @@ -23,8 +23,8 @@ def process_messages(mocker, estimator, lag_frames, n_frames, vego=20.0, rejecti for i in range(n_frames): t = i * estimator.dt - desired_la = np.cos(t) * 0.1 - actual_la = np.cos(t - lag_frames * estimator.dt) * 0.1 + desired_la = np.cos(10 * t) * 0.1 + actual_la = np.cos(10 * (t - lag_frames * estimator.dt)) * 0.1 # if sample is masked out, set it to desired value (no lag) rejected = random.uniform(0, 1) < rejection_threshold From 9622427044253bacfa314e47fa0808779252116e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 23 May 2025 16:32:19 -0700 Subject: [PATCH 321/477] car interfaces: improve steering pressed signal (#35333) * bump * update refs --- opendbc_repo | 2 +- selfdrive/test/process_replay/ref_commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index afb464bd7a..c495fa822c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit afb464bd7a3134ab5511d26c0ca9060cb2793ed5 +Subproject commit c495fa822c845b0bfc6e1511ced3425d111f3fe4 diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 0ca95ec5fa..5f5cb89089 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -280cd05fe44e47474d04f2cf7435fb0000a562e5 \ No newline at end of file +5c8f6c360b66f269a24d519e699addf9e9bcaefe \ No newline at end of file From 70644e23172e6952da70978ba0fb91d96e7ae195 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 23 May 2025 16:54:50 -0700 Subject: [PATCH 322/477] VW: low steer speed alert in CarState (#35331) * move to opendbc * func * clean up * bump * move cmt * bump --- opendbc_repo | 2 +- selfdrive/car/car_specific.py | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index c495fa822c..0c8b12328e 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c495fa822c845b0bfc6e1511ced3425d111f3fe4 +Subproject commit 0c8b12328ec4fcb7481806f62cd90310573d4c0c diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index c5edb484af..ed3a0cb9d8 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -2,7 +2,6 @@ from cereal import car, log import cereal.messaging as messaging from opendbc.car import DT_CTRL, structs from opendbc.car.interfaces import MAX_CTRL_SPEED -from opendbc.car.volkswagen.values import CarControllerParams as VWCarControllerParams from openpilot.selfdrive.selfdrived.events import Events @@ -112,14 +111,6 @@ class CarSpecificEvents: events = self.create_common_events(CS, CS_prev, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic], pcm_enable=self.CP.pcmCruise) - # Low speed steer alert hysteresis logic - if (self.CP.minSteerSpeed - 1e-3) > VWCarControllerParams.DEFAULT_MIN_STEER_SPEED and CS.vEgo < (self.CP.minSteerSpeed + 1.): - self.low_speed_alert = True - elif CS.vEgo > (self.CP.minSteerSpeed + 2.): - self.low_speed_alert = False - if self.low_speed_alert: - events.add(EventName.belowSteerSpeed) - if self.CP.openpilotLongitudinalControl: if CS.vEgo < self.CP.minEnableSpeed + 0.5: events.add(EventName.belowEngageSpeed) From ce8ea163037fa3da32df17062e0f7d94eaa366fb Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 23 May 2025 18:50:20 -0700 Subject: [PATCH 323/477] update release notes --- RELEASES.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 5730877f94..acdb709c9b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,13 +1,11 @@ -Version 0.9.9 (2025-05-15) +Version 0.9.9 (2025-05-23) ======================== * New driving model - * New training architecture supervised by MLSIM -* Steering actuator delay is now learned online + * New training architecture using parts from MLSIM +* Steering actuation delay is now learned online +* Hyundai Nexo 2021 support thanks to sunnyhaibin! * Tesla Model 3 and Y support thanks to lukasloetkolben! * Lexus RC 2023 support thanks to nelsonjchen! -* Coming soon - * New Honda models - * Bigger vision model Version 0.9.8 (2025-02-28) ======================== From 97ad175243a01f89151b0156afac3a2fa2b3be3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Sat, 24 May 2025 04:31:17 +0200 Subject: [PATCH 324/477] Reapply "Enable online lag learning (#34968)" (#35327) * Revert "Revert "Enable online lag learning (#34968)" (#35322)" This reverts commit 9c40c487292956618c58a4e77b4c9d825d5ae91b. * Update ref commit * Update ref * Update ref commit --- selfdrive/locationd/torqued.py | 7 ++++--- selfdrive/modeld/modeld.py | 4 ++-- selfdrive/test/process_replay/process_replay.py | 4 ++-- selfdrive/test/process_replay/ref_commit | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index fcaa19df73..5ed86e457f 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -52,7 +52,7 @@ class TorqueBuckets(PointBuckets): class TorqueEstimator(ParameterEstimator): def __init__(self, CP, decimated=False, track_all_points=False): self.hist_len = int(HISTORY / DT_MDL) - self.lag = CP.steerActuatorDelay + .2 # from controlsd + self.lag = 0.0 self.track_all_points = track_all_points # for offline analysis, without max lateral accel or max steer torque filters if decimated: self.min_bucket_points = MIN_BUCKET_POINTS / 10 @@ -175,7 +175,8 @@ class TorqueEstimator(ParameterEstimator): self.raw_points["steer_override"].append(msg.steeringPressed) elif which == "liveCalibration": self.calibrator.feed_live_calib(msg) - + elif which == "liveDelay": + self.lag = msg.lateralDelay # calculate lateral accel from past steering torque elif which == "livePose": if len(self.raw_points['steer_torque']) == self.hist_len: @@ -241,7 +242,7 @@ def main(demo=False): config_realtime_process([0, 1, 2, 3], 5) pm = messaging.PubMaster(['liveTorqueParameters']) - sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose'], poll='livePose') + sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose') params = Params() estimator = TorqueEstimator(messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)) diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 96520a4b15..1e3d1782e6 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -229,7 +229,7 @@ def main(demo=False): # messaging pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) - sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"]) + sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) publish_state = PublishState() params = Params() @@ -256,7 +256,6 @@ def main(demo=False): # TODO this needs more thought, use .2s extra for now to estimate other delays # TODO Move smooth seconds to action function - lat_delay = CP.steerActuatorDelay + .2 + LAT_SMOOTH_SECONDS long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -300,6 +299,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) + lat_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index d87a330fdd..8de2e60055 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -564,7 +564,7 @@ CONFIGS = [ ), ProcessConfig( proc_name="torqued", - pubs=["livePose", "liveCalibration", "carState", "carControl", "carOutput"], + pubs=["livePose", "liveCalibration", "liveDelay", "carState", "carControl", "carOutput"], subs=["liveTorqueParameters"], ignore=["logMonoTime"], init_callback=get_car_params_callback, @@ -573,7 +573,7 @@ CONFIGS = [ ), ProcessConfig( proc_name="modeld", - pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "driverMonitoringState", "carState", "carControl"], + pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "liveDelay", "driverMonitoringState", "carState", "carControl"], subs=["modelV2", "drivingModelData", "cameraOdometry"], ignore=["logMonoTime", "modelV2.frameDropPerc", "modelV2.modelExecutionTime", "drivingModelData.frameDropPerc", "drivingModelData.modelExecutionTime"], should_recv_callback=ModeldCameraSyncRcvCallback(), diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 5f5cb89089..b091f2b689 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -5c8f6c360b66f269a24d519e699addf9e9bcaefe \ No newline at end of file +6ad91de126c2a9827b1a0536068fd79d9244eaa2 \ No newline at end of file From c24f3498079a8b5f7202ccd62dc786e0e0380bce Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 23 May 2025 19:43:17 -0700 Subject: [PATCH 325/477] GM: low steer speed alert in CarState (#35332) * simple * rm * bump to master * update refs * update refs --- opendbc_repo | 2 +- selfdrive/car/car_specific.py | 2 -- selfdrive/test/process_replay/ref_commit | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index 0c8b12328e..3ae4dc13cd 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0c8b12328ec4fcb7481806f62cd90310573d4c0c +Subproject commit 3ae4dc13cd9e628672a01651896021214b7c3c17 diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index ed3a0cb9d8..d441c648ff 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -104,8 +104,6 @@ class CarSpecificEvents: events.add(EventName.belowEngageSpeed) if CS.cruiseState.standstill: events.add(EventName.resumeRequired) - if CS.vEgo < self.CP.minSteerSpeed: - events.add(EventName.belowSteerSpeed) elif self.CP.brand == 'volkswagen': events = self.create_common_events(CS, CS_prev, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic], diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index b091f2b689..e692f56eba 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -6ad91de126c2a9827b1a0536068fd79d9244eaa2 \ No newline at end of file +9e2fe2942fbf77f24bccdbef15893831f9c0b390 \ No newline at end of file From b51043770b1accf00a05f74cd4ec71a9405b6ba2 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 24 May 2025 16:20:57 -0400 Subject: [PATCH 326/477] `CarControlSP`: live params (#943) * car: initialize sunnypilot interface immediately * init * Revert "car: initialize sunnypilot interface immediately" This reverts commit 2e0fc4d87eb312a3a74a61bb8cbc780e33efbfc7. * show int * show int * work properly with cereal * unused * in its own module * bump * not yet * not yet * list comp and use structs directly * allow default key if needed * lint * send it with None * bump --- cereal/custom.capnp | 6 ++++ opendbc_repo | 2 +- selfdrive/car/helpers.py | 1 + selfdrive/controls/controlsd.py | 29 +++++++++++---- .../selfdrive/controls/controlsd_ext.py | 18 +++++++--- .../selfdrive/controls/lib/param_store.py | 35 +++++++++++++++++++ 6 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 sunnypilot/selfdrive/controls/lib/param_store.py diff --git a/cereal/custom.capnp b/cereal/custom.capnp index eb932b484e..503a2be86c 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -167,6 +167,12 @@ struct CarParamsSP @0x80ae746ee2596b11 { struct CarControlSP @0xa5cd762cd951a455 { mads @0 :ModularAssistiveDrivingSystem; + params @1 :List(Param); + + struct Param { + key @0 :Text; + value @1 :Text; + } } struct BackupManagerSP @0xf98d843bfd7004a3 { diff --git a/opendbc_repo b/opendbc_repo index ac3d40b757..52d2957b47 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit ac3d40b7570d85ba4f8056ac542a10cc8a82ccea +Subproject commit 52d2957b47360e36c02d626ed02981123af79b24 diff --git a/selfdrive/car/helpers.py b/selfdrive/car/helpers.py index 3f7a322c08..6ebacfc976 100644 --- a/selfdrive/car/helpers.py +++ b/selfdrive/car/helpers.py @@ -55,5 +55,6 @@ def convert_carControlSP(struct: capnp.lib.capnp._DynamicStructReader) -> struct struct_dataclass = structs.CarControlSP(**remove_deprecated({k: v for k, v in struct_dict.items() if not isinstance(k, dict)})) struct_dataclass.mads = structs.ModularAssistiveDrivingSystem(**remove_deprecated(struct_dict.get('mads', {}))) + struct_dataclass.params = [structs.CarControlSP.Param(**remove_deprecated(p)) for p in struct_dict.get('params', [])] return struct_dataclass diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index a5d97c3db0..576e73b7e6 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 import math +import threading +import time from typing import SupportsFloat from cereal import car, log @@ -36,7 +38,7 @@ class Controls(ControlsExt): cloudlog.info("controlsd got CarParams") # Initialize sunnypilot controlsd extension - ControlsExt.__init__(self, self.params) + ControlsExt.__init__(self, self.CP, self.params) self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP) @@ -222,14 +224,27 @@ class Controls(ControlsExt): cc_send.carControl = CC self.pm.send('carControl', cc_send) + def params_thread(self, evt): + while not evt.is_set(): + self.get_params_sp() + + time.sleep(0.1) + def run(self): rk = Ratekeeper(100, print_delay_threshold=None) - while True: - self.update() - CC, lac_log = self.state_control() - self.publish(CC, lac_log) - self.run_ext(self.sm, self.pm) - rk.monitor_time() + e = threading.Event() + t = threading.Thread(target=self.params_thread, args=(e,)) + try: + t.start() + while True: + self.update() + CC, lac_log = self.state_control() + self.publish(CC, lac_log) + self.run_ext(self.sm, self.pm) + rk.monitor_time() + finally: + e.set() + t.join() def main(): diff --git a/sunnypilot/selfdrive/controls/controlsd_ext.py b/sunnypilot/selfdrive/controls/controlsd_ext.py index 68131001ad..4978c01332 100644 --- a/sunnypilot/selfdrive/controls/controlsd_ext.py +++ b/sunnypilot/selfdrive/controls/controlsd_ext.py @@ -4,16 +4,22 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ - import cereal.messaging as messaging from cereal import custom +from opendbc.car import structs from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog +from openpilot.sunnypilot.selfdrive.controls.lib.param_store import ParamStore class ControlsExt: - def __init__(self, params: Params): + def __init__(self, CP: structs.CarParams, params: Params): + self.CP = CP + self.params = params + self.param_store = ParamStore(self.CP) + self.get_params_sp() + cloudlog.info("controlsd_ext is waiting for CarParamsSP") self.CP_SP = messaging.log_from_bytes(params.get("CarParamsSP", block=True), custom.CarParamsSP) cloudlog.info("controlsd_ext got CarParamsSP") @@ -21,6 +27,9 @@ class ControlsExt: self.sm_services_ext = ['selfdriveStateSP'] self.pm_services_ext = ['carControlSP'] + def get_params_sp(self) -> None: + self.param_store.update(self.params) + @staticmethod def get_lat_active(sm: messaging.SubMaster) -> bool: ss_sp = sm['selfdriveStateSP'] @@ -31,13 +40,14 @@ class ControlsExt: # MADS not available, use stock state to engage return bool(sm['selfdriveState'].active) - @staticmethod - def state_control_ext(sm: messaging.SubMaster) -> custom.CarControlSP: + def state_control_ext(self, sm: messaging.SubMaster) -> custom.CarControlSP: CC_SP = custom.CarControlSP.new_message() # MADS state CC_SP.mads = sm['selfdriveStateSP'].mads + CC_SP.params = self.param_store.publish() + return CC_SP @staticmethod diff --git a/sunnypilot/selfdrive/controls/lib/param_store.py b/sunnypilot/selfdrive/controls/lib/param_store.py new file mode 100644 index 0000000000..b2701bc010 --- /dev/null +++ b/sunnypilot/selfdrive/controls/lib/param_store.py @@ -0,0 +1,35 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import capnp + +from cereal import custom + +from opendbc.car import structs +from openpilot.common.params import Params + + +class ParamStore: + keys: list[str] + values: dict[str, str] + + def __init__(self, CP: structs.CarParams): + universal_params: list[str] = [] + brand_params: list[str] = [] + + self.keys = universal_params + brand_params + self.values = {} + + def update(self, params: Params) -> None: + self.values = {k: params.get(k, encoding='utf8') or "0" for k in self.keys} + + def publish(self) -> list[capnp.lib.capnp._DynamicStructBuilder]: + params_list: list[capnp.lib.capnp._DynamicStructBuilder] = [] + + for k in self.keys: + params_list.append(custom.CarControlSP.Param(key=k, value=self.values[k])) + + return params_list From b939efc7736bd94e4fb7e731a123da1c76823c42 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 25 May 2025 00:51:58 -0400 Subject: [PATCH 327/477] Hyundai CAN: re-enable auto-enable radar tracks (#945) * Hyundai CAN: re-enable auto-enable radar tracks * check if radar tracks config bit is already enabled * only show success message if already enabled * test if it would actually skip * 5 bytes only * tested skipping, revert * lint * move to opendbc right after fping * more * lint * bump --- common/params_keys.h | 4 -- opendbc_repo | 2 +- selfdrive/car/card.py | 1 - selfdrive/selfdrived/selfdrived.py | 1 - selfdrive/ui/qt/offroad/developer_panel.cc | 17 -------- selfdrive/ui/qt/offroad/developer_panel.h | 1 - sunnypilot/selfdrive/car/car_specific.py | 10 ----- sunnypilot/selfdrive/car/interfaces.py | 45 +--------------------- sunnypilot/selfdrive/selfdrived/events.py | 4 -- 9 files changed, 3 insertions(+), 82 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index d3a9ea7d6d..cb6f50cacd 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -165,10 +165,6 @@ inline static std::unordered_map keys = { // sunnypilot car specific params {"HyundaiLongitudinalTuning", PERSISTENT}, - {"HyundaiRadarTracks", PERSISTENT}, - {"HyundaiRadarTracksConfirmed", PERSISTENT}, - {"HyundaiRadarTracksPersistent", PERSISTENT}, - {"HyundaiRadarTracksToggle", PERSISTENT}, {"DynamicExperimentalControl", PERSISTENT}, }; diff --git a/opendbc_repo b/opendbc_repo index 52d2957b47..c5d42d5493 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 52d2957b47360e36c02d626ed02981123af79b24 +Subproject commit c5d42d54934a55808bea1a8acb081cfc62a273bc diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index fa4b10e0d1..64e180367b 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -274,7 +274,6 @@ class Car: # Initialize CarInterface, once controls are ready # TODO: this can make us miss at least a few cycles when doing an ECU knockout self.CI.init(self.CP, self.CP_SP, *self.can_callbacks) - sunnypilot_interfaces.init_interfaces(self.CP, self.CP_SP, self.params, *self.can_callbacks) # signal pandad to switch to car safety mode self.params.put_bool_nonblocking("ControlsReady", True) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 0b825869f4..2ebc2815bb 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -545,7 +545,6 @@ class SelfdriveD(CruiseHelper): self.personality = self.read_personality_param() self.mads.read_params() - self.car_events_sp.read_params() time.sleep(0.1) def run(self): diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc index 4d9d8a6ce7..3e9647d2cf 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.cc +++ b/selfdrive/ui/qt/offroad/developer_panel.cc @@ -45,18 +45,6 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) { }); addItem(experimentalLongitudinalToggle); - // TODO-SP: Move to Vehicles panel when ported back - hyundaiRadarTracksToggle = new ParamControl( - "HyundaiRadarTracksToggle", - tr("Hyundai: Enable Radar Tracks"), - tr("Enable this to attempt to enable radar tracks for Hyundai, Kia, and Genesis models equipped with the supported Mando SCC radar. " - "This allows sunnypilot to use radar data for improved lead tracking and overall longitudinal performance."), ""); - hyundaiRadarTracksToggle->setConfirmation(true, false); - QObject::connect(hyundaiRadarTracksToggle, &ParamControl::toggleFlipped, [=](bool state) { - updateToggles(offroad); - }); - addItem(hyundaiRadarTracksToggle); - enableGithubRunner = new ParamControl("EnableGithubRunner", tr("Enable GitHub runner service"), tr("Enables or disables the github runner service."), ""); addItem(enableGithubRunner); @@ -96,9 +84,6 @@ void DeveloperPanel::updateToggles(bool _offroad) { capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); cereal::CarParams::Reader CP = cmsg.getRoot(); - auto hyundai = CP.getBrand() == "hyundai"; - auto hyundai_mando_radar = hyundai && (CP.getFlags() & 4096); - if (!CP.getAlphaLongitudinalAvailable() || is_release) { params.remove("AlphaLongitudinalEnabled"); experimentalLongitudinalToggle->setEnabled(false); @@ -112,11 +97,9 @@ void DeveloperPanel::updateToggles(bool _offroad) { experimentalLongitudinalToggle->setVisible(CP.getAlphaLongitudinalAvailable() && !is_release); longManeuverToggle->setEnabled(hasLongitudinalControl(CP) && _offroad); - hyundaiRadarTracksToggle->setVisible(hyundai_mando_radar && hasLongitudinalControl(CP)); } else { longManeuverToggle->setEnabled(false); experimentalLongitudinalToggle->setVisible(false); - hyundaiRadarTracksToggle->setVisible(false); } experimentalLongitudinalToggle->refresh(); diff --git a/selfdrive/ui/qt/offroad/developer_panel.h b/selfdrive/ui/qt/offroad/developer_panel.h index 591e5f0fdf..c15b574f5a 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.h +++ b/selfdrive/ui/qt/offroad/developer_panel.h @@ -19,7 +19,6 @@ private: ButtonControl* errorLogBtn; ParamControl* longManeuverToggle; ParamControl* experimentalLongitudinalToggle; - ParamControl* hyundaiRadarTracksToggle; ParamControl* enableGithubRunner; bool is_release; bool offroad = false; diff --git a/sunnypilot/selfdrive/car/car_specific.py b/sunnypilot/selfdrive/car/car_specific.py index 649aceca01..7e6f300d73 100644 --- a/sunnypilot/selfdrive/car/car_specific.py +++ b/sunnypilot/selfdrive/car/car_specific.py @@ -23,12 +23,6 @@ class CarSpecificEventsSP: self.params = params self.low_speed_alert = False - self.hyundai_radar_tracks = self.params.get_bool("HyundaiRadarTracks") - self.hyundai_radar_tracks_confirmed = self.params.get_bool("HyundaiRadarTracksConfirmed") - - def read_params(self): - self.hyundai_radar_tracks = self.params.get_bool("HyundaiRadarTracks") - self.hyundai_radar_tracks_confirmed = self.params.get_bool("HyundaiRadarTracksConfirmed") def update(self, CS: structs.CarState, events: Events): events_sp = EventsSP() @@ -48,8 +42,4 @@ class CarSpecificEventsSP: if self.low_speed_alert: events.add(EventName.belowSteerSpeed) - if self.CP.brand == 'hyundai': - if self.hyundai_radar_tracks and not self.hyundai_radar_tracks_confirmed: - events_sp.add(EventNameSP.hyundaiRadarTracksConfirmed) - return events_sp diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index f2019f5ed7..e2a232a3ef 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -5,12 +5,8 @@ This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ -from opendbc.car import Bus, structs -from opendbc.car.can_definitions import CanRecvCallable, CanSendCallable -from opendbc.car.car_helpers import can_fingerprint +from opendbc.car import structs from opendbc.car.interfaces import CarInterfaceBase -from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR -from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP from openpilot.common.params import Params @@ -26,6 +22,7 @@ def log_fingerprint(CP: structs.CarParams) -> None: else: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) + def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None) -> None: if params is None: @@ -63,47 +60,9 @@ def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs CP_SP.neuralNetworkLateralControl.fuzzyFingerprint = not exact_match -def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None) -> None: - if params is None: - params = Params() - - if CP.brand == 'hyundai': - if CP.flags & HyundaiFlags.MANDO_RADAR and CP.radarUnavailable: - # Having this automatic without a toggle causes a weird process replay diff because - # somehow it sees fewer logs than intended - if params.get_bool("HyundaiRadarTracksToggle"): - CP_SP.flags |= HyundaiFlagsSP.ENABLE_RADAR_TRACKS.value - if params.get_bool("HyundaiRadarTracks"): - CP.radarUnavailable = False - - def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None: CP = CI.CP CP_SP = CI.CP_SP _initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params) _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) - _initialize_radar_tracks(CP, CP_SP, params) - - -def _enable_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, can_recv: CanRecvCallable, - params: Params) -> None: - if CP.brand == 'hyundai': - if CP_SP.flags & HyundaiFlagsSP.ENABLE_RADAR_TRACKS: - can_recv() - _, fingerprint = can_fingerprint(can_recv) - radar_unavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in HYUNDAI_DBC[CP.carFingerprint] - - radar_tracks = params.get_bool("HyundaiRadarTracks") - radar_tracks_persistent = params.get_bool("HyundaiRadarTracksPersistent") - - params.put_bool_nonblocking("HyundaiRadarTracksConfirmed", radar_tracks) - - if not radar_tracks_persistent: - params.put_bool_nonblocking("HyundaiRadarTracks", not radar_unavailable) - params.put_bool_nonblocking("HyundaiRadarTracksPersistent", True) - - -def init_interfaces(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params, - can_recv: CanRecvCallable, can_send: CanSendCallable): - _enable_radar_tracks(CP, CP_SP, can_recv, params) diff --git a/sunnypilot/selfdrive/selfdrived/events.py b/sunnypilot/selfdrive/selfdrived/events.py index 67672649c5..d81345a14e 100644 --- a/sunnypilot/selfdrive/selfdrived/events.py +++ b/sunnypilot/selfdrive/selfdrived/events.py @@ -122,10 +122,6 @@ EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = { ET.NO_ENTRY: NoEntryAlert("Controls Mismatch: Lateral"), }, - EventNameSP.hyundaiRadarTracksConfirmed: { - ET.PERMANENT: NormalPermanentAlert("Radar tracks available. Restart the car to initialize") - }, - EventNameSP.experimentalModeSwitched: { ET.WARNING: NormalPermanentAlert("Experimental Mode Switched", duration=1.5) }, From 3c36374bed2387c86b3d7231aa56e4f5a64ac066 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 25 May 2025 10:50:09 +0200 Subject: [PATCH 328/477] ci: build upstream (tinnygrad) models from our pipeline (#498) * Add job to fetch and upload model artifacts Introduced a new `get_model` job to retrieve and upload model artifacts as part of the workflow. Updated the `build_model` job to download and clean up model files before building, ensuring an isolated and streamlined process. These changes improve modularity and artifact handling in the CI pipeline. * Refine model file handling in build workflow Restrict deletion and download commands to ".onnx" files to prevent unintended removal or overwriting of non-ONNX files. This ensures a more targeted and safer workflow for handling model artifacts. * Update model artifact handling in build workflow Changed artifact upload path to include only .onnx files and commented out the deletion of existing models. Also added a dependency for the build stage to ensure proper execution sequence. * Update build workflow to refine model artifact handling Removed unused code for deleting models and fixed the artifact path. Added a reference file logging custom name and upstream branch for better traceability. These changes streamline the workflow and improve clarity. * Set dynamic run-name for sunnypilot build workflow Added a customizable `run-name` field in the sunnypilot build model workflow. This allows more informative and dynamic naming based on input parameters such as `custom_name` or `upstream_branch`. Improves clarity and traceability in workflow runs. * Add customization options and metadata generation to build Introduce new inputs for file name prefix and 20Hz model specification in the workflow. Enable renaming of model files, generation of file hashes, and creation of a `metadata.json` file containing build details. This improves flexibility and enhances artifact information management. * remove EOF * No need for this hack anymore * Add support for compiling ONNX models to Tinygrad format * Add model directory environment variable and metadata generation script * Fix quoting in model generator script command * Refactor conditional flag for 20Hz input in build model script * meh * Add powersave management to build model workflow * Add environment variable setup to build model workflow * make it path call it a day * kill me * kill me x2 * mmm * mmm * Generate metadata.pkl * Generate metadata.pkl * ignore failure on getting model metadata * Cleanup the existing onnxs * Fix rm command syntax in build model script * Cleaning a little * Add optional file name parameter to generate_metadata function * update * stg * linter * more formatting * Update sunnypilot model build workflow: improve custom naming, handle 20Hz default, and add commit date as output. * Update commit date format in workflow script Changed the commit date format in sunnypilot-build-model.yaml to a more human-readable format ('Month DD, YYYY'). This improves readability and consistency in the workflow outputs. * Fix output naming in sunnypilot-build-model workflow Corrected the output name from 'model_date' to 'commit_date' to match its usage. This ensures consistency and prevents potential errors in the workflow. Add debug echo for model date in GitHub workflow This change adds a debug echo statement to display the model date in the sunnypilot-build-model workflow. It helps verify the correct commit date is being set during the workflow process. * Update BUILD_DIR path in sunnypilot workflow Replaced hardcoded BUILD_DIR with a dynamic path using `github.workspace`. This enhances flexibility and ensures compatibility with different environments or runners. * Revert "Update BUILD_DIR path in sunnypilot workflow" This reverts commit 376971b616298f0ef72b4995868c25cbe12f0cae. * Refactor model metadata generation to use short names Replaced `file_name` with generated `short_name` for metadata and file naming. Introduced `create_short_name` function to simplify short name creation from custom names. Updated scripts and workflow to reflect the new parameter and improve metadata handling. * `Use timezone-aware UTC timestamp for build_time` Updated the code to use `datetime.now(timezone.utc)` instead of `datetime.utcnow()`. This ensures that the generated timestamps are timezone-aware, improving consistency and correctness in the output. * Refactor short name generation logic in model metadata Enhanced the `create_short_name` function to better handle name conversion by incorporating new rules for handling parentheses, alphanumeric words, and version pairs. Updated related variable names in metadata functions for improved clarity and consistency. * Fix syntax error in assigning 'models' key in metadata Corrected a typo where 'models' was missing quotes, causing it to be interpreted as a variable instead of a string key. This ensures proper metadata dictionary structure. Update model metadata structure and JSON generation Revised metadata to include detailed download URIs and a type field for artifacts. Enhanced the JSON generator with new fields like environment, runner, and overrides for better configurability. These changes aim to improve metadata clarity and compatibility with downstream processes. * Refactor variable name from "escaped_name" to "short_name" Renamed "escaped_name" to "short_name" across function parameters and internal logic for clarity and consistency. This improves readability and better aligns with its use case in the metadata generation process. No functional changes were introduced. * Refactor variable names and improve type annotations. Updated parameter names to align with Python naming conventions, enhancing readability and consistency. Added type annotations for function parameters to improve code clarity and maintainability. * Refactor type annotations and clarify comments. Updated type annotations to use "Str" for consistency with custom typing conventions. Improved clarity of a comment describing the normal case logic in the shortening function. * Fix dict formatting in model_generator.py Moved the "overrides" key for better alignment and consistency in the dictionary format. This improves readability without changing functionality. Refactor model short name generation logic Simplify and clarify the `create_short_name` function by improving readability and condensing comments. Adjust casing and truncation logic for single-word cases and handle versioned names more intuitively. * Ensure custom file names are converted to lowercase. When renaming files using a custom short name, enforce lowercase conversion to maintain consistency and prevent potential issues with case-sensitive file systems. This change applies to both the tinygrad and metadata file names. * Use UTC alias for timezone in datetime imports Replaced `timezone.utc` with `UTC` from the `datetime` module for clarity and consistency. This simplifies the representation of time zones and aligns with Python's updated best practices. --------- Co-authored-by: Jason Wen --- .github/workflows/sunnypilot-build-model.yaml | 142 +++++++++++------- release/ci/model_generator.py | 131 ++++++++++++++++ 2 files changed, 220 insertions(+), 53 deletions(-) create mode 100755 release/ci/model_generator.py diff --git a/.github/workflows/sunnypilot-build-model.yaml b/.github/workflows/sunnypilot-build-model.yaml index e845d80c66..dc02d1d8d2 100644 --- a/.github/workflows/sunnypilot-build-model.yaml +++ b/.github/workflows/sunnypilot-build-model.yaml @@ -5,6 +5,8 @@ env: OUTPUT_DIR: ${{ github.workspace }}/output SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache UPSTREAM_REPO: "commaai/openpilot" + TINYGRAD_PATH: ${{ github.workspace }}/tinygrad_repo + MODELS_DIR: ${{ github.workspace }}/selfdrive/modeld/models on: workflow_dispatch: @@ -15,35 +17,55 @@ on: default: 'master' type: string custom_name: - description: 'Custom name for the model' - required: false - type: string - file_name: - description: 'File name prefix for the model files' + description: 'Custom name for the model (no date, only name)' required: false type: string is_20hz: description: 'Is this a 20Hz model' required: false type: boolean - default: false + default: true run-name: Build model [${{ inputs.custom_name || inputs.upstream_branch }}] from ref [${{ inputs.upstream_branch }}] jobs: - build_model: - runs-on: self-hosted - + get_model: + runs-on: ubuntu-latest + outputs: + model_date: ${{ steps.commit-date.outputs.model_date }} steps: - uses: actions/checkout@v4 with: repository: ${{ env.UPSTREAM_REPO }} ref: ${{ github.event.inputs.upstream_branch }} submodules: recursive + - name: Get commit date + id: commit-date + run: | + # Get the commit date in YYYY-MM-DD format + commit_date=$(git log -1 --format=%cd --date=format:'%B %d, %Y') + echo "model_date=${commit_date}" >> $GITHUB_OUTPUT + cat $GITHUB_OUTPUT + - run: git lfs pull + - name: 'Upload Artifact' + uses: actions/upload-artifact@v4 + with: + name: models + path: ${{ github.workspace }}/selfdrive/modeld/models/*.onnx + + build_model: + runs-on: self-hosted + needs: get_model + env: + MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }}) + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive - run: git lfs pull - - name: Cache SCons uses: actions/cache@v4 with: @@ -60,31 +82,63 @@ jobs: scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }} scons-${{ runner.os }}-${{ runner.arch }} + - name: Set environment variables + id: set-env + run: | + # Set up common environment + source /etc/profile; + export UV_PROJECT_ENVIRONMENT=${HOME}/venv + export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT + printenv >> $GITHUB_ENV + if [[ "${{ runner.debug }}" == "1" ]]; then + cat $GITHUB_OUTPUT + fi + - name: Setup build environment run: | mkdir -p "${BUILD_DIR}/" sudo find $BUILD_DIR/ -mindepth 1 -delete echo "Starting build stage..." - echo "Building from: ${{ env.UPSTREAM_REPO }} branch: ${{ github.event.inputs.upstream_branch }}" - - - name: Patch SConstruct to pass arbitrary cache - run: | - sed -i.bak 's#cache_dir =#default_cache_dir =#' ${{ github.workspace }}/SConstruct - printf '/default_cache_dir/a\\\ncache_dir = ARGUMENTS.get("cache_dir", default_cache_dir)\n' | sed -i.bak -f - ${{ github.workspace }}/SConstruct - cat ${{ github.workspace }}/SConstruct + echo "BUILD_DIR: ${BUILD_DIR}" + echo "CI_DIR: ${CI_DIR}" + echo "VERSION: ${{ steps.set-env.outputs.version }}" + echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}" + echo "VIRTUAL_ENV: ${VIRTUAL_ENV}" + echo "-------" + if [[ "${{ runner.debug }}" == "1" ]]; then + printenv + fi + PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable + rm -rf ${{ env.MODELS_DIR }}/*.onnx + + - name: Download model artifacts + uses: actions/download-artifact@v4 + with: + name: models + path: ${{ github.workspace }}/selfdrive/modeld/models - name: Build Model run: | source /etc/profile export UV_PROJECT_ENVIRONMENT=${HOME}/venv export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT - scons -j$(nproc) cache_dir=${{ env.SCONS_CACHE_DIR }} ${{ github.workspace }}/selfdrive/modeld + export PYTHONPATH="${PYTHONPATH}:${{ env.TINYGRAD_PATH }}" + + # Loop through all .onnx files + find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do + base_name=$(basename "$onnx_file" .onnx) + output_file="${{ env.MODELS_DIR }}/${base_name}_tinygrad.pkl" + + echo "Compiling: $onnx_file -> $output_file" + QCOM=1 python3 "${{ env.TINYGRAD_PATH }}/examples/openpilot/compile3.py" "$onnx_file" "$output_file" + QCOM=1 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true + done - name: Prepare Output run: | - sudo rm -rf ${OUTPUT_DIR} - mkdir -p ${OUTPUT_DIR} - + sudo rm -rf ${{ env.OUTPUT_DIR }} + mkdir -p ${{ env.OUTPUT_DIR }} + # Copy the model files rsync -avm \ --include='*.dlc' \ @@ -94,40 +148,22 @@ jobs: --exclude='*' \ --delete-excluded \ --chown=comma:comma \ - ./selfdrive/modeld/models/ ${OUTPUT_DIR}/ - - # Rename files if file_name is provided - if [ ! -z "${{ inputs.file_name }}" ]; then - mv ${OUTPUT_DIR}/supercombo.thneed ${OUTPUT_DIR}/supercombo-${{ inputs.file_name }}.thneed - mv ${OUTPUT_DIR}/supercombo_metadata.pkl ${OUTPUT_DIR}/supercombo-${{ inputs.file_name }}_metadata.pkl - fi - - # Calculate SHA256 hashes - HASH=$(sha256sum ${OUTPUT_DIR}/supercombo*.thneed | cut -d' ' -f1) - METADATA_HASH=$(sha256sum ${OUTPUT_DIR}/supercombo*_metadata.pkl | cut -d' ' -f1) - - # Create metadata.json - cat > ${OUTPUT_DIR}/metadata.json << EOF - { - "display_name": "${{ inputs.custom_name || inputs.upstream_branch }}", - "full_name": "${{ inputs.file_name || 'default' }}", - "is_20hz": ${{ inputs.is_20hz || false }}, - "files": { - "drive_model": { - "file_name": "$(basename ${OUTPUT_DIR}/supercombo*.thneed)", - "sha256": "${HASH}" - }, - "metadata": { - "file_name": "$(basename ${OUTPUT_DIR}/supercombo*_metadata.pkl)", - "sha256": "${METADATA_HASH}" - } - }, - "ref": "${{ inputs.upstream_branch }}", - "build_time": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" - } + ${{ env.MODELS_DIR }}/ ${{ env.OUTPUT_DIR }}/ + + python3 "${{ github.workspace }}/release/ci/model_generator.py" \ + --model-dir "${{ env.MODELS_DIR }}" \ + --output-dir "${{ env.OUTPUT_DIR }}" \ + --custom-name "${{ env.MODEL_NAME }}" \ + --upstream-branch "${{ inputs.upstream_branch }}" \ + ${{ inputs.is_20hz && '--is-20hz' || '' }} - name: Upload Build Artifacts uses: actions/upload-artifact@v4 with: - name: model-${{ github.event.inputs.custom_name || github.event.inputs.upstream_branch }}-${{ github.run_number }} + name: model-${{ env.MODEL_NAME }}-${{ github.run_number }} path: ${{ env.OUTPUT_DIR }} + + - name: Re-enable powersave + if: always() + run: | + PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable diff --git a/release/ci/model_generator.py b/release/ci/model_generator.py new file mode 100755 index 0000000000..069b8e41af --- /dev/null +++ b/release/ci/model_generator.py @@ -0,0 +1,131 @@ +import os +import sys +import hashlib +import json +import re +from pathlib import Path +from datetime import datetime, UTC + + +def create_short_name(full_name): + # Remove parentheses and extract alphanumeric words + clean_name = re.sub(r'\([^)]*\)', '', full_name) + words = [re.sub(r'[^a-zA-Z0-9]', '', word) for word in clean_name.split() if re.sub(r'[^a-zA-Z0-9]', '', word)] + + if len(words) == 1: + # If there's only one word, return it as is, lowercased, truncated to 8 characters + truncated = words[0][:8] + return truncated.lower() if truncated.isupper() else truncated + + # Handle special case: Name + Version (e.g., "Word A1" -> "WordA1") + if len(words) == 2 and re.match(r'^[A-Za-z]\d+$', words[1]): + first_word = words[0].lower() if words[0].isupper() else words[0] + return (first_word + words[1])[:8] + + # Normal case: first letter and trailing numbers from each word + result = ''.join(word if word.isdigit() else word[0] + (re.search(r'\d+$', word) or [''])[0] for word in words) + return result[:8] + + +def generate_metadata(model_path: Path, output_dir: Path, short_name: str): + model_path = model_path + output_path = output_dir + base = model_path.stem + + # Define output files for tinygrad and metadata + tinygrad_file = output_path / f"{base}_tinygrad.pkl" + metadata_file = output_path / f"{base}_metadata.pkl" + + if not tinygrad_file.exists() or not metadata_file.exists(): + print(f"Error: Missing files for model {base} ({tinygrad_file} or {metadata_file})", file=sys.stderr) + return + + # Calculate the sha256 hashes + with open(tinygrad_file, 'rb') as f: + tinygrad_hash = hashlib.sha256(f.read()).hexdigest() + + with open(metadata_file, 'rb') as f: + metadata_hash = hashlib.sha256(f.read()).hexdigest() + + # Rename the files if a custom file name is provided + if short_name: + tinygrad_file = tinygrad_file.rename(output_path / f"{base}_{short_name.lower()}_tinygrad.pkl") + metadata_file = metadata_file.rename(output_path / f"{base}_{short_name.lower()}_metadata.pkl") + + # Build the metadata structure + model_metadata = { + "type": base.split("_")[-1] if "dmonitoring" not in base else "dmonitoring", + "artifact": { + "file_name": tinygrad_file.name, + "download_uri": { + "url": "https://gitlab.com/sunnypilot/public/docs.sunnypilot.ai/-/raw/main/", + "sha256": tinygrad_hash + } + }, + "metadata": { + "file_name": metadata_file.name, + "download_uri": { + "url": "https://gitlab.com/sunnypilot/public/docs.sunnypilot.ai/-/raw/main/", + "sha256": metadata_hash + } + } + } + + # Return model metadata + return model_metadata + + +def create_metadata_json(models: list, output_dir: Path, custom_name=None, short_name=None, is_20hz=False, upstream_branch="unknown"): + metadata_json = { + "short_name": short_name, + "display_name": custom_name or upstream_branch, + "is_20hz": is_20hz, + "ref": upstream_branch, + "environment": "development", + "runner": "tinygrad", + "build_time": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"), + "models": models, + "overrides": {}, + "index": -1, + "minimum_selector_version": "-1", + "generation": "-1", + } + + # Write metadata to output_dir + with open(output_dir / "metadata.json", "w") as f: + json.dump(metadata_json, f, indent=2) + + print(f"Generated metadata.json with {len(models)} models.") + + +if __name__ == "__main__": + import argparse + import glob + + parser = argparse.ArgumentParser(description="Generate metadata for model files") + parser.add_argument("--model-dir", default="./models", help="Directory containing ONNX model files") + parser.add_argument("--output-dir", default="./output", help="Output directory for metadata") + parser.add_argument("--custom-name", help="Custom display name for the model") + parser.add_argument("--is-20hz", action="store_true", help="Whether this is a 20Hz model") + parser.add_argument("--upstream-branch", default="unknown", help="Upstream branch name") + args = parser.parse_args() + + # Find all ONNX files in the given directory + model_paths = glob.glob(os.path.join(args.model_dir, "*.onnx")) + if not model_paths: + print(f"No ONNX files found in {args.model_dir}", file=sys.stderr) + sys.exit(1) + + _output_dir = Path(args.output_dir) + _output_dir.mkdir(exist_ok=True, parents=True) + _models = [] + + for _model_path in model_paths: + _model_metadata = generate_metadata(Path(_model_path), _output_dir, create_short_name(args.custom_name)) + if _model_metadata: + _models.append(_model_metadata) + + if _models: + create_metadata_json(_models, _output_dir, args.custom_name, create_short_name(args.custom_name), args.is_20hz, args.upstream_branch) + else: + print("No models processed.", file=sys.stderr) From 9b7502bd857476e536616214fc6c5f566c2a52ce Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Sun, 25 May 2025 17:10:16 +0200 Subject: [PATCH 329/477] model: Refactor modeld with modular runners and split model support (#877) * Refactor model runner methods for improved abstraction. Moved slicing logic to a private `_slice_outputs` method and decoupled `_run_model` for clearer subclass implementation. Removed redundant `output` attribute in `ModelState` to streamline data handling. * Add output parsing to model_runner and remove duplicate logic Integrates an output parser directly into `model_runner` for streamlined inference and parsing. Removes redundant parser initialization from `modeld` to avoid duplication and enhance maintainability. * Reordering * linter * linter * Refactor model handling with `ModelData` abstraction Introduce a `ModelData` class to encapsulate model and metadata logic, improving code clarity and modularity. Refactor `ModelRunner` to manage multiple models and add conditional handling for fallback scenarios. Adjust `TinygradRunner` to validate and use the new `ModelData` structure. * Refactor model handling to use dictionary-based structure Replaces the model list with a dictionary keyed by model type to improve clarity and maintainability. Updates related logic and ensures consistent handling of model metadata and inputs. Adds `slice_outputs` implementation to the `TinygradRunner` for proper output parsing. * Refactor model runners to support policy and vision separation Introduced `TinygradPolicyRunner`, `TinygradVisionRunner`, and `TinygradSplitRunner` to enable separate handling of policy and vision models. Updated `TinygradRunner` initialization and input preparation to accommodate modular processing. Adjusted `modeld` to utilize the new runners, ensuring compatibility with separated model workflows. * Refactor model runner initialization and simplify logic Introduce `get_model_runner` to centralize model runner selection logic, replacing multiple conditional instantiations. Simplify the handling of model metadata by removing fallback logic and restructuring output slicing to enforce proper loading of model data. These changes improve code maintainability and clarity. * Refactor model data access for TinygradRunner initialization Update references to access nested artifact properties and align with structural changes to the model data schema. This simplifies input shapes handling and ensures compatibility with updated model attributes. * Refactor imports and clean up redundant code. Removed unused imports and improved formatting for clarity and maintainability. These changes simplify the codebase by eliminating unnecessary dependencies and ensuring consistency. * Refactor model output parsing with specialized parsers. Introduce abstract `_parse_outputs` method to standardize parsing logic. Add `SplitParser` for specialized parsing in `TinygradVisionRunner` and `TinygradPolicyRunner`. This improves modularity and paves the way for easier parser customization. * Add parser for model output processing in modeld_v2 Introduce a new `Parser` class to handle parsing and processing of model outputs, including MDN, binary cross-entropy, and categorical cross-entropy outputs. This modularizes the logic, improves clarity, and prepares for handling various types of model data. * Add `input_shapes` property to model runners Introduce a new `input_shapes` property in the abstract base class and its implementation in derived classes. This provides a standardized way to access the input shapes of models, improving clarity and consistency in the model runners. * Refactor model runner to use private `_model_data` attribute Replaced public `model_data` with private `_model_data` for improved encapsulation. Updated all references and property accessors accordingly. Simplified model type handling by using raw types where applicable. * Remove debug print statement from model_runner.py The unnecessary `print(model_type)` statement was removed as it served no functional purpose in the code. This improves code cleanliness and avoids unintended console output during execution. * Refactor `_parse_outputs` call in `run_model`. Replaced the use of `self.parser.parse_outputs` with `self._parse_outputs` for clarity and consistency. Updated method signature to align with the revised usage. * Refactor model output parsing for clarity and scope separation Moved specific parsing logic (e.g., lane_lines, lead) from `parse_model_outputs` to `parse_policy_outputs` to better align with functional responsibilities. This improves modularity and readability while maintaining existing functionality. * Refactor model_runner to simplify result handling Renamed variable `result` to `parsed_result` for clarity and removed unnecessary slicing during model output parsing. These changes improve code readability and maintain consistency within the `run_model` method. * Adjust _parse_outputs method signature in model_runner Updated the method signature of _parse_outputs to accept a single np.ndarray instead of a dictionary. This aligns with the intended data structure and ensures consistency across subclasses implementing this abstract method. * Refactor ModelRunner to enforce abstract base class compliance Updated `ModelRunner` and its subclasses to properly inherit from `ABC` while refactoring methods to ensure compliance with Python's abstract base class standards. Streamlined the handling of `_parse_outputs` and added a new `input_shapes` property for improved functionality. * Fix buffer length issue in 20Hz model initialization Adjusted `FULL_HISTORY_BUFFER_LEN` by adding +1 for `full_features_20Hz` to address compatibility issues with the current FoF model. Added a comment noting potential failure for other models with this adjustment. * Refactor TinygradRunner to remove abstract methods. Simplified the TinygradRunner class by removing unnecessary @abstractmethod decorators and redundant method definitions. This streamlines the code and aligns it more effectively with its current usage and implementation. * Refactor model runner classes and enhance type annotations Simplified model runner implementations, added type annotations, and improved code readability and maintainability. Introduced new type definitions, updated metadata handling, and standardized input/output parsing across all runner classes. Minor comment update in `modeld.py` for clarity. * Refactor model runner classes with detailed docstrings. Enhanced class and function docstrings across model_runner.py for better clarity and maintainability. Descriptions now include detailed explanations of attributes, purposes, and workflows to aid understanding and future development. * Refactor model runner classes and add TICI hardware optimization Simplified and clarified class definitions, comments, and functionality for ModelRunner subclasses. Introduced the use of QCOM environment variable on TICI for potential hardware acceleration. Enhanced input/output handling and error reporting across Tinygrad and ONNX implementations. * Update parser import and usage to use CombinedParser Replaced the Parser class with CombinedParser in model_runner.py. This change ensures consistency with the updated parsing logic, aligning with the latest requirements for combined model output handling. * Refactor TinygradRunner hierarchy for modular parsers Reorganized the TinygradRunner and its specialized runners (Vision, Policy, and Supercombo) into a cleaner, modular structure using composable classes. This consolidates parser logic, removes redundancy, and simplifies initialization by leveraging a shared base class with a dictionary-based parser method. * Refactor model runners to use ModularRunner as abstract base. Introduce a new `ModularRunner` class to enforce a consistent interface across model runners. Updated existing runners, including `ModelRunner`, `SupercomboTinygrad`, `PolicyTinygrad`, and `VisionTinygrad`, to extend `ModularRunner`. Added abstract methods and properties to enhance modularity and code maintainability. * Refactor model runners into modular components. This commit separates the logic for Tinygrad, ONNX, and split runners into clearly defined modules and components. It introduces `PolicyTinygrad`, `VisionTinygrad`, `SupercomboTinygrad`, and centralized helpers for cleaner architecture. The changes improve modularity and maintainability of the model running and parsing workflows. * Simplify imports and clean up unused code in ONNXRunner. Removed unused imports and redundant environmental variables to streamline the codebase. Consolidated necessary imports and organized type definitions for improved readability and maintenance. * Standardize imports and add model data validation. Updated import paths to ensure consistency across modules by using `openpilot` as the base. Introduced validation in `_parse_outputs` methods to handle cases where `_model_data` is not initialized, preventing potential runtime errors. * Remove unused import and fix whitespace in runners The unused import `ModelData` was removed from `tinygrad_runner.py` to clean up the code. Additionally, extraneous whitespace was corrected in `onnx_runner.py` for improved readability and consistency. * Remove unnecessary blank line in import statements Cleaned up import section by removing an extra blank line. This helps maintain consistency and adheres to code style conventions. * BROKEN!! Staging code but its not gonna work. Also I realized we need to run the split models in 2 stages because the output of one is immediately needed for the input of the other. We might handle it inside of the model_runner instead * update smooth * Revert "update smooth" This reverts commit c335712e6e1ee189459ce34dfc9d4028feb9470f. * match case made this very hard to read. * shouldnt be there * Refactor to allow TR (soon TM) * TR 7 is .1 * metadata * .2=3 * Remove redundant comments and clean up conditional blocks in modeld.py. * Undoing wrong buffer * Refactor model initialization and adjust ONNX runner import Reorganized numpy input buffer initialization and updated `temporal_idxs` logic for better clarity and efficiency. Conditional import for ONNXRunner added for non-TICI platforms to optimize imports. These changes improve maintainability and compatibility across platforms. * Update CURRENT_SELECTOR_VERSION to 4 Bump the CURRENT_SELECTOR_VERSION constant from 3 to 4 to reflect changes in the selector logic or requirements. This ensures compatibility with the updated selector version while maintaining the minimum required version as 2. * Add output_slices property to model runners Introduce output_slices property to provide access to the output slices for individual and combined models. This ensures consistent handling of output slices across vision and policy models, improving modularity and usability. * Refactor imports to use SplitModelConstants consistently Updated import references to use the renamed `SplitModelConstants` class for consistency across files. This change ensures clarity and better alignment with the updated class naming convention. * Refactor buffer initialization and desired curvature handling Refactored model input buffer initialization for improved clarity and consistency, leveraging dynamic shape calculations. Extracted `process_desired_curvature` method to encapsulate logic for handling 3D and non-3D cases. Simplified temporal index generation and related calculations for better maintainability. * Refactor desire reshape dims logic in modeld.py Adjust logic for setting desire reshape dimensions to handle `is_20hz_3d` separately. This improves clarity and ensures proper handling of different model runner configurations. * Fix off-by-one error in full_desire buffer initialization The full_desire buffer length was mistakenly set to full_history_buffer_len + 1. This change corrects it to match the intended full_history_buffer_len, ensuring proper alignment with other buffers. * Simplify desire reshape logic by removing unused condition. Removed the `is_20hz_3d` condition and associated reshape logic since it is no longer needed. This streamlines the code and avoids unnecessary checks for unused configurations. * Refactor buffer initialization for 20Hz model variants Reorganized buffer initialization logic to prioritize the 20Hz_3D condition. This improves clarity and ensures specific handling of different 20Hz configurations. Adjusted the order of conditions to streamline execution flow. * Refactor: Move `get_action_from_model` and constants to `Model` class to improve encapsulation and readability. * 12 line reshaping red diff * .2 to match old models delay * mypy fixes * Revert "12 line reshaping red diff" This reverts commit 8c7280f629043f0485749e2536a20af74c9209b2. * mypy * remove this * Fix desired curvature for models which do not output desired_curvature * fix FoF * flip policy and vision outs to allow FoF and tomb raider to live in harmony using conditional `if 'this' in outs:'` * noqa * single * sunnypilot modeld.py * action * overrides methodology * combine split outputs to its own method * comments * Fix static checker line length * static will fail on line length lines: 286, 206, 70 - 77, 159, 168 * Address E501 line length violations * This will make TR better while not effecting FoF/VFF at all * Reduce this to one conditional and just call normally in vision/policy * Align with upstream in our own way. * check for desired curvature in outputs first * outputs * Use a cleaner import method * Fix output * Clean up some values * Only call on init * slight cleanup * names!!!!!!!!! * Refactor overrides structure to support key-value pairs. The overrides structure now uses a list of key-value pairs instead of fixed lat/long fields. This change improves flexibility, allowing dynamic addition of override parameters. Code adjustments ensure backward compatibility and consistent behavior throughout the application. * Refactor: Use local variable for SplitModelConstants Introduce a local `constants` variable to replace repeated access to `SplitModelConstants`. This simplifies code readability and adheres to linter recommendations for line length. * Refactor model constant handling to improve modularity Replaced direct usage of model constants with dynamic access through model runners for better scalability and maintainability. This change centralizes constant definitions, reduces redundancy, and ensures clearer integration with different model types. --------- Co-authored-by: discountchubbs Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen --- cereal/custom.capnp | 8 +- .../controls/lib/longitudinal_planner.py | 4 - sunnypilot/modeld/constants.py | 5 +- sunnypilot/modeld/fill_model_msg.py | 15 +- sunnypilot/modeld/modeld.py | 32 +++- sunnypilot/modeld_v2/fill_model_msg.py | 35 ++-- sunnypilot/modeld_v2/get_model_metadata.py | 24 ++- sunnypilot/modeld_v2/model_runner.py | 142 -------------- sunnypilot/modeld_v2/modeld.py | 121 ++++++++---- .../modeld_v2/parse_model_outputs_split.py | 127 +++++++++++++ sunnypilot/models/fetcher.py | 14 +- sunnypilot/models/helpers.py | 2 +- sunnypilot/models/runners/constants.py | 18 ++ sunnypilot/models/runners/helpers.py | 35 ++++ sunnypilot/models/runners/model_runner.py | 176 ++++++++++++++++++ sunnypilot/models/runners/onnx/onnx_runner.py | 62 ++++++ .../models/runners/tinygrad/model_types.py | 59 ++++++ .../runners/tinygrad/tinygrad_runner.py | 129 +++++++++++++ sunnypilot/models/split_model_constants.py | 94 ++++++++++ .../controls/lib/longitudinal_planner.py | 2 - 20 files changed, 879 insertions(+), 225 deletions(-) delete mode 100644 sunnypilot/modeld_v2/model_runner.py create mode 100644 sunnypilot/modeld_v2/parse_model_outputs_split.py create mode 100644 sunnypilot/models/runners/constants.py create mode 100644 sunnypilot/models/runners/helpers.py create mode 100644 sunnypilot/models/runners/model_runner.py create mode 100644 sunnypilot/models/runners/onnx/onnx_runner.py create mode 100644 sunnypilot/models/runners/tinygrad/model_types.py create mode 100644 sunnypilot/models/runners/tinygrad/tinygrad_runner.py create mode 100644 sunnypilot/models/split_model_constants.py diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 503a2be86c..8ae77875f2 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -78,6 +78,11 @@ struct ModelManagerSP @0xaedffd8f31e7b55d { stock @2; } + struct Override { + key @0 :Text; + value @1 :Text; + } + struct ModelBundle { index @0 :UInt32; internalName @1 :Text; @@ -88,8 +93,9 @@ struct ModelManagerSP @0xaedffd8f31e7b55d { environment @6 :Text; runner @7 :Runner; is20hz @8 :Bool; - ref @9 :Text; # New field + ref @9 :Text; minimumSelectorVersion @10 :UInt32; + overrides @11 :List(Override); } } diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 377ce7c2cb..4e842f8d1b 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -178,10 +178,6 @@ class LongitudinalPlanner(LongitudinalPlannerSP): output_a_target = min(output_a_target_mpc, output_a_target_e2e) self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc - if not self.is_stock: - # To support non Tomb Raider models - output_a_target, self.output_should_stop = output_a_target_mpc, output_should_stop_mpc - for idx in range(2): accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05) self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1]) diff --git a/sunnypilot/modeld/constants.py b/sunnypilot/modeld/constants.py index 8f63afa461..18abc6c960 100644 --- a/sunnypilot/modeld/constants.py +++ b/sunnypilot/modeld/constants.py @@ -1,7 +1,8 @@ import numpy as np def index_function(idx, max_val=192, max_idx=32): - return (max_val) * ((idx/max_idx)**2) + return max_val * ((idx/max_idx)**2) + class ModelConstants: # time and distance indices @@ -63,6 +64,7 @@ class ModelConstants: POLY_PATH_DEGREE = 4 + # model outputs slices class Plan: POSITION = slice(0, 3) @@ -71,6 +73,7 @@ class Plan: T_FROM_CURRENT_EULER = slice(9, 12) ORIENTATION_RATE = slice(12, 15) + class Meta: ENGAGED = slice(0, 1) # next 2, 4, 6, 8, 10 seconds diff --git a/sunnypilot/modeld/fill_model_msg.py b/sunnypilot/modeld/fill_model_msg.py index 1c40425bdd..608f24424d 100644 --- a/sunnypilot/modeld/fill_model_msg.py +++ b/sunnypilot/modeld/fill_model_msg.py @@ -50,7 +50,7 @@ def fill_xyz_poly(builder, degree, x, y, z): builder.zCoefficients = coeffs[:, 2].tolist() def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._DynamicStructBuilder, - net_output_data: dict[str, np.ndarray], publish_state: PublishState, + net_output_data: dict[str, np.ndarray], action: log.ModelDataV2.Action, publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float, valid: bool, v_ego: float, steer_delay: float, meta_const) -> None: @@ -76,8 +76,11 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D driving_model_data.frameDropPerc = frame_drop_perc driving_model_data.modelExecutionTime = model_execution_time - action = driving_model_data.action - action.desiredCurvature = desired_curvature + # Populate drivingModelData.action + driving_model_data_action = driving_model_data.action + driving_model_data_action.desiredAcceleration = action.desiredAcceleration + driving_model_data_action.shouldStop = action.shouldStop + driving_model_data_action.desiredCurvature = desired_curvature modelV2 = extended_msg.modelV2 modelV2.frameId = vipc_frame_id @@ -111,8 +114,10 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D fill_xyz_poly(poly_path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) # lateral planning - action = modelV2.action - action.desiredCurvature = desired_curvature + modelV2_action = modelV2.action + modelV2_action.desiredAcceleration = action.desiredAcceleration + modelV2_action.shouldStop = action.shouldStop + modelV2_action.desiredCurvature = desired_curvature # times at X_IDXS according to model plan PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N diff --git a/sunnypilot/modeld/modeld.py b/sunnypilot/modeld/modeld.py index 744b90465b..2ac3f5700e 100755 --- a/sunnypilot/modeld/modeld.py +++ b/sunnypilot/modeld/modeld.py @@ -18,12 +18,15 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value + from openpilot.sunnypilot.modeld.runners import ModelRunner, Runtime from openpilot.sunnypilot.modeld.parse_model_outputs import Parser from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState -from openpilot.sunnypilot.modeld.constants import ModelConstants +from openpilot.sunnypilot.modeld.constants import ModelConstants, Plan +from openpilot.sunnypilot.models.helpers import get_active_bundle, get_model_path, load_metadata, prepare_inputs, load_meta_constants from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext -from openpilot.sunnypilot.models.helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants + PROCESS_NAME = "selfdrive.modeld.modeld_snpe" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -34,6 +37,7 @@ MODEL_PATHS = { METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl' + class FrameMeta: frame_id: int = 0 timestamp_sof: int = 0 @@ -55,6 +59,10 @@ class ModelState: self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) + bundle = get_active_bundle() + overrides = {override.key: override.value for override in bundle.overrides} + self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".2")) + self.LONG_SMOOTH_SECONDS = float(overrides.get('long', ".0")) model_paths = get_model_path() self.model_metadata = load_metadata() @@ -118,6 +126,15 @@ class ModelState: self.inputs['prev_desired_curv'][-1:] = outputs['desired_curvature'][0, :] return outputs + def get_action_from_model(self, model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, + long_action_t: float) -> log.ModelDataV2.Action: + plan = model_output['plan'][0] + desired_accel, should_stop = get_accel_from_plan(plan[:, Plan.VELOCITY][:, 0], plan[:, Plan.ACCELERATION][:, 0], ModelConstants.T_IDXS, + action_t=long_action_t) + desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, self.LONG_SMOOTH_SECONDS) + + return log.ModelDataV2.Action(desiredAcceleration=float(desired_accel), shouldStop=bool(should_stop)) + def main(demo=False): cloudlog.warning("modeld init") @@ -158,7 +175,7 @@ def main(demo=False): # messaging pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) - sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"]) + sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) publish_state = PublishState() params = Params() @@ -184,8 +201,10 @@ def main(demo=False): cloudlog.info("modeld got CarParams: %s", CP.brand) - # TODO this needs more thought, use .2s extra for now to estimate other delays - steer_delay = CP.steerActuatorDelay + .2 + # Enable lagd support for sunnypilot modeld + steer_delay = sm["liveDelay"].lateralDelay + model.LAT_SMOOTH_SECONDS + long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS + prev_action = log.ModelDataV2.Action() DH = DesireHelper() @@ -280,7 +299,8 @@ def main(demo=False): modelv2_send = messaging.new_message('modelV2') drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - fill_model_msg(drivingdata_send, modelv2_send, model_output, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, + action = model.get_action_from_model(model_output, prev_action, long_delay + DT_MDL) + fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, v_ego, steer_delay, model.meta) diff --git a/sunnypilot/modeld_v2/fill_model_msg.py b/sunnypilot/modeld_v2/fill_model_msg.py index c6129936ca..411b8177f3 100644 --- a/sunnypilot/modeld_v2/fill_model_msg.py +++ b/sunnypilot/modeld_v2/fill_model_msg.py @@ -3,30 +3,21 @@ import capnp import numpy as np from cereal import log from openpilot.sunnypilot.modeld_v2.constants import ModelConstants, Plan -from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED +from openpilot.selfdrive.controls.lib.drive_helpers import get_curvature_from_plan SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') ConfidenceClass = log.ModelDataV2.ConfidenceClass -def curv_from_psis(psi_target, psi_rate, vego, delay): - vego = np.clip(vego, MIN_SPEED, np.inf) - curv_from_psi = psi_target / (vego * delay) # epsilon to prevent divide-by-zero - return 2 * curv_from_psi - psi_rate / vego +def get_curvature_from_output(output, vego, lat_action_t, current_generation=None): + if current_generation != 11: + if desired_curv := output.get('desired_curvature'): # If the model outputs the desired curvature, use that directly + return float(desired_curv[0, 0]) - -def get_curvature_from_plan(plan, vego, delay): - psi_target = np.interp(delay, ModelConstants.T_IDXS, plan[:, Plan.T_FROM_CURRENT_EULER][:, 2]) - psi_rate = plan[:, Plan.ORIENTATION_RATE][0, 2] - return curv_from_psis(psi_target, psi_rate, vego, delay) - - -def get_curvature_from_output(output, vego, delay): - if desired_curv := output.get('desired_curvature'): # If the model outputs the desired curvature, use that directly - return float(desired_curv[0, 0]) - - return float(get_curvature_from_plan(output['plan'][0], vego, delay)) + plan_output = output['plan'][0] + return float(get_curvature_from_plan(plan_output[:, Plan.T_FROM_CURRENT_EULER][:, 2], plan_output[:, Plan.ORIENTATION_RATE][:, 2], + ModelConstants.T_IDXS, vego, lat_action_t)) class PublishState: @@ -76,7 +67,7 @@ def fill_lane_line_meta(builder, lane_lines, lane_line_probs): builder.rightProb = lane_line_probs[2] def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._DynamicStructBuilder, - net_output_data: dict[str, np.ndarray], v_ego: float, delay: float, + net_output_data: dict[str, np.ndarray], action: log.ModelDataV2.Action, publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float, valid: bool, model_meta) -> None: @@ -85,15 +76,13 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D extended_msg.valid = valid base_msg.valid = valid - desired_curvature = float(get_curvature_from_output(net_output_data, v_ego, delay)) - driving_model_data = base_msg.drivingModelData driving_model_data.frameId = vipc_frame_id driving_model_data.frameIdExtra = vipc_frame_id_extra driving_model_data.frameDropPerc = frame_drop_perc driving_model_data.modelExecutionTime = model_execution_time - driving_model_data.action.desiredCurvature = desired_curvature + driving_model_data.action = action modelV2 = extended_msg.modelV2 modelV2.frameId = vipc_frame_id @@ -126,8 +115,8 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D # poly path fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T) - # lateral planning - modelV2.action.desiredCurvature = desired_curvature + # action (includes lateral planning now) + modelV2.action = action # times at X_IDXS according to model plan PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N diff --git a/sunnypilot/modeld_v2/get_model_metadata.py b/sunnypilot/modeld_v2/get_model_metadata.py index 144860204f..e0b5adc51b 100755 --- a/sunnypilot/modeld_v2/get_model_metadata.py +++ b/sunnypilot/modeld_v2/get_model_metadata.py @@ -4,22 +4,34 @@ import pathlib import onnx import codecs import pickle +from typing import Any + def get_name_and_shape(value_info:onnx.ValueInfoProto) -> tuple[str, tuple[int,...]]: shape = tuple([int(dim.dim_value) for dim in value_info.type.tensor_type.shape.dim]) name = value_info.name return name, shape + +def get_metadata_value_by_name(model:onnx.ModelProto, name:str) -> str | Any: + for prop in model.metadata_props: + if prop.key == name: + return prop.value + return None + + if __name__ == "__main__": model_path = pathlib.Path(sys.argv[1]) model = onnx.load(str(model_path)) - i = [x.key for x in model.metadata_props].index('output_slices') - output_slices = model.metadata_props[i].value + output_slices = get_metadata_value_by_name(model, 'output_slices') + assert output_slices is not None, 'output_slices not found in metadata' - metadata = {} - metadata['output_slices'] = pickle.loads(codecs.decode(output_slices.encode(), "base64")) - metadata['input_shapes'] = dict([get_name_and_shape(x) for x in model.graph.input]) - metadata['output_shapes'] = dict([get_name_and_shape(x) for x in model.graph.output]) + metadata = { + 'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'), + 'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")), + 'input_shapes': dict([get_name_and_shape(x) for x in model.graph.input]), + 'output_shapes': dict([get_name_and_shape(x) for x in model.graph.output]) + } metadata_path = model_path.parent / (model_path.stem + '_metadata.pkl') with open(metadata_path, 'wb') as f: diff --git a/sunnypilot/modeld_v2/model_runner.py b/sunnypilot/modeld_v2/model_runner.py deleted file mode 100644 index 10c80b8769..0000000000 --- a/sunnypilot/modeld_v2/model_runner.py +++ /dev/null @@ -1,142 +0,0 @@ -import os -import pickle -from abc import ABC, abstractmethod -import numpy as np - -from cereal import custom -from openpilot.sunnypilot.modeld_v2 import MODEL_PATH, MODEL_PKL_PATH, METADATA_PATH -from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLMem -from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES -from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address -from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser -from openpilot.system.hardware import TICI -from openpilot.system.hardware.hw import Paths - -from openpilot.sunnypilot.models.helpers import get_active_bundle -from tinygrad.tensor import Tensor - -if TICI: - os.environ['QCOM'] = '1' - -SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') -CUSTOM_MODEL_PATH = Paths.model_root() -ModelManager = custom.ModelManagerSP - - -class ModelRunner(ABC): - """Abstract base class for model runners that defines the interface for running ML models.""" - - def __init__(self): - """Initialize the model runner with paths to model and metadata files.""" - metadata_path = METADATA_PATH - self.is_20hz = None - self._drive_model = None - self._metadata_model = None - - if bundle := get_active_bundle(): - bundle_models = {model.type.raw: model for model in bundle.models} - self._drive_model = bundle_models.get(ModelManager.Model.Type.supercombo) - self._metadata_model = self._drive_model.metadata - self.is_20hz = bundle.is20hz - - # Override the metadata path if a metadata model is found in the active bundle - if self._metadata_model: - metadata_path = f"{CUSTOM_MODEL_PATH}/{self._metadata_model.fileName}" - - with open(metadata_path, 'rb') as f: - self.model_metadata = pickle.load(f) - - self.input_shapes = self.model_metadata['input_shapes'] - self.output_slices = self.model_metadata['output_slices'] - self.inputs: dict = {} - self.parser = Parser() - - @abstractmethod - def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict: - """Prepare inputs for model inference.""" - raise NotImplementedError - - @abstractmethod - def _run_model(self): - """Run model inference with prepared inputs.""" - raise NotImplementedError("This method should be implemented in subclasses.") - - def _slice_outputs(self, model_outputs: np.ndarray) -> dict: - """Slice model outputs according to metadata configuration.""" - parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()} - if SEND_RAW_PRED: - parsed_outputs['raw_pred'] = model_outputs.copy() - return parsed_outputs - - def run_model(self) -> dict[str, np.ndarray]: - """Run model inference with prepared inputs and parse outputs.""" - result: dict[str, np.ndarray] = self.parser.parse_outputs(self._slice_outputs(self._run_model())) - return result - - -class TinygradRunner(ModelRunner): - """Tinygrad implementation of model runner for TICI hardware.""" - - def __init__(self): - super().__init__() - - model_pkl_path = MODEL_PKL_PATH - if self._drive_model: - model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.artifact.fileName}" - assert model_pkl_path.endswith('_tinygrad.pkl'), f"Invalid model file: {model_pkl_path} for TinygradRunner" - - # Load Tinygrad model - with open(model_pkl_path, "rb") as f: - try: - self.model_run = pickle.load(f) - except FileNotFoundError as e: - assert "/dev/kgsl-3d0" not in str(e), "Model was built on C3 or C3X, but is being loaded on PC" - raise - - self.input_to_dtype = {} - self.input_to_device = {} - - for idx, name in enumerate(self.model_run.captured.expected_names): - self.input_to_dtype[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][2] # 2 is the dtype - self.input_to_device[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][3] # 3 is the device - - def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict: - # Initialize image tensors if not already done - for key in imgs_cl: - if TICI and key not in self.inputs: - self.inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=self.input_to_dtype[key]) - elif not TICI: - shape = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]) - self.inputs[key] = Tensor(shape, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize() - - # Update numpy inputs - for key, value in numpy_inputs.items(): - if key not in imgs_cl: - self.inputs[key] = Tensor(value, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize() - - return self.inputs - - def _run_model(self): - return self.model_run(**self.inputs).numpy().flatten() - - -class ONNXRunner(ModelRunner): - """ONNX implementation of model runner for non-TICI hardware.""" - - def __init__(self): - super().__init__() - self.runner = make_onnx_cpu_runner(MODEL_PATH) - - self.input_to_nptype = { - model_input.name: ORT_TYPES_TO_NP_TYPES[model_input.type] - for model_input in self.runner.get_inputs() - } - - def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict: - self.inputs = numpy_inputs - for key in imgs_cl: - self.inputs[key] = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=self.input_to_nptype[key]) - return self.inputs - - def _run_model(self): - return self.runner.run(None, self.inputs)[0].flatten() diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index a52d844aeb..4c8acafcad 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -1,7 +1,4 @@ #!/usr/bin/env python3 -from openpilot.system.hardware import TICI - -# import time import numpy as np import cereal.messaging as messaging @@ -13,20 +10,22 @@ from opendbc.car.car_helpers import get_demo_car_params from openpilot.common.swaglog import cloudlog from openpilot.common.params import Params from openpilot.common.filter_simple import FirstOrderFilter -from openpilot.common.realtime import config_realtime_process +from openpilot.common.realtime import config_realtime_process, DT_MDL from openpilot.common.transformations.camera import DEVICE_CAMERAS from openpilot.common.transformations.model import get_warp_matrix from openpilot.system import sentry from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper -from openpilot.sunnypilot.modeld_v2.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState -from openpilot.sunnypilot.modeld_v2.constants import ModelConstants -from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext +from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value +from openpilot.sunnypilot.modeld_v2.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState, get_curvature_from_output +from openpilot.sunnypilot.modeld_v2.constants import Plan +from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants -from openpilot.sunnypilot.modeld_v2.model_runner import ONNXRunner, TinygradRunner + +from openpilot.sunnypilot.models.helpers import get_active_bundle +from openpilot.sunnypilot.models.runners.helpers import get_model_runner PROCESS_NAME = "selfdrive.modeld.modeld" -LAT_SMOOTH_SECONDS = 0.0 class FrameMeta: @@ -38,23 +37,32 @@ class FrameMeta: if vipc is not None: self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof + class ModelState: frames: dict[str, DrivingModelFrame] inputs: dict[str, np.ndarray] prev_desire: np.ndarray # for tracking the rising edge of the pulse + temporal_idxs: slice | np.ndarray def __init__(self, context: CLContext): try: - self.model_runner = TinygradRunner() if TICI else ONNXRunner() + self.model_runner = get_model_runner() + self.constants = self.model_runner.constants except Exception as e: cloudlog.exception(f"Failed to initialize model runner: {str(e)}") + raise + + model_bundle = get_active_bundle() + self.generation = model_bundle.generation + overrides = {override.key: override.value for override in model_bundle.overrides} + + self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".2")) + self.LONG_SMOOTH_SECONDS = float(overrides.get('long', ".0")) + self.MIN_LAT_CONTROL_SPEED = 0.3 buffer_length = 5 if self.model_runner.is_20hz else 2 self.frames = {'input_imgs': DrivingModelFrame(context, buffer_length), 'big_input_imgs': DrivingModelFrame(context, buffer_length)} - self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) - if self.model_runner.is_20hz: - self.full_features_buffer = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32) - self.full_desire = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32) + self.prev_desire = np.zeros(self.constants.DESIRE_LEN, dtype=np.float32) # img buffers are managed in openCL transform code self.numpy_inputs = {} @@ -63,11 +71,19 @@ class ModelState: if key not in self.frames: # Managed by opencl self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32) - if self.model_runner.is_20hz: + if self.model_runner.is_20hz_3d: # split models + self.full_features_buffer = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.FEATURE_LEN), dtype=np.float32) + self.full_desire = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.DESIRE_LEN), dtype=np.float32) + self.full_prev_desired_curv = np.zeros((1, self.constants.FULL_HISTORY_BUFFER_LEN, self.constants.PREV_DESIRED_CURV_LEN), dtype=np.float32) + self.temporal_idxs = slice(-1-(self.constants.TEMPORAL_SKIP*(self.constants.INPUT_HISTORY_BUFFER_LEN-1)), None, self.constants.TEMPORAL_SKIP) + elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: + self.full_features_buffer = np.zeros((self.constants.FULL_HISTORY_BUFFER_LEN + 1, self.constants.FEATURE_LEN), dtype=np.float32) + self.full_desire = np.zeros((self.constants.FULL_HISTORY_BUFFER_LEN + 1, self.constants.DESIRE_LEN), dtype=np.float32) num_elements = self.numpy_inputs['features_buffer'].shape[1] step_size = int(-100 / num_elements) - self.full_features_buffer_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] - self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, self.numpy_inputs['desire'].shape[2]) + self.temporal_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1] + self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, + self.numpy_inputs['desire'].shape[2]) def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None: @@ -76,11 +92,15 @@ class ModelState: new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) self.prev_desire[:] = inputs['desire'] - if self.model_runner.is_20hz: + if self.model_runner.is_20hz_3d: # split models + self.full_desire[0,:-1] = self.full_desire[0,1:] + self.full_desire[0,-1] = new_desire + self.numpy_inputs['desire'][:] = self.full_desire.reshape((1, self.constants.INPUT_HISTORY_BUFFER_LEN, self.constants.TEMPORAL_SKIP, -1)).max(axis=2) + elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: # 20hz supercombo self.full_desire[:-1] = self.full_desire[1:] self.full_desire[-1] = new_desire self.numpy_inputs['desire'][:] = self.full_desire.reshape(self.desire_reshape_dims).max(axis=2) - else: + else: # not 20hz length = inputs['desire'].shape[0] self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:] self.numpy_inputs['desire'][0, -1, :length] = new_desire[:length] @@ -101,11 +121,15 @@ class ModelState: # Run model inference outputs = self.model_runner.run_model() - if self.model_runner.is_20hz: + if self.model_runner.is_20hz_3d: # split models + self.full_features_buffer[0, :-1] = self.full_features_buffer[0, 1:] + self.full_features_buffer[0, -1] = outputs['hidden_state'][0, :] + self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs] + elif self.model_runner.is_20hz and not self.model_runner.is_20hz_3d: # 20hz supercombo self.full_features_buffer[:-1] = self.full_features_buffer[1:] self.full_features_buffer[-1] = outputs['hidden_state'][0, :] - self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.full_features_buffer_idxs] - else: + self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.temporal_idxs] + else: # not 20hz feature_len = outputs['hidden_state'].shape[1] self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:] self.numpy_inputs['features_buffer'][0, -1, :feature_len] = outputs['hidden_state'][0, :feature_len] @@ -119,11 +143,36 @@ class ModelState: input_name_prev = 'prev_desired_curv' if input_name_prev is not None: - length = outputs['desired_curvature'][0].size - self.numpy_inputs[input_name_prev][0, :-length, 0] = self.numpy_inputs[input_name_prev][0, length:, 0] - self.numpy_inputs[input_name_prev][0, -length:, 0] = outputs['desired_curvature'][0] + self.process_desired_curvature(outputs, input_name_prev) return outputs + def process_desired_curvature(self, outputs, input_name_prev): + if self.model_runner.is_20hz_3d: # split models + self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:] + self.full_prev_desired_curv[0,-1,:] = outputs['desired_curvature'][0, :] + self.numpy_inputs[input_name_prev][:] = self.full_prev_desired_curv[0, self.temporal_idxs] + if self.generation == 11: + self.numpy_inputs[input_name_prev][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs] + else: + length = outputs['desired_curvature'][0].size + self.numpy_inputs[input_name_prev][0, :-length, 0] = self.numpy_inputs[input_name_prev][0, length:, 0] + self.numpy_inputs[input_name_prev][0, -length:, 0] = outputs['desired_curvature'][0] + + def get_action_from_model(self, model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action, + lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action: + plan = model_output['plan'][0] + desired_accel, should_stop = get_accel_from_plan(plan[:, Plan.VELOCITY][:, 0], plan[:, Plan.ACCELERATION][:, 0], self.constants.T_IDXS, + action_t=long_action_t) + desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, self.LONG_SMOOTH_SECONDS) + + desired_curvature = get_curvature_from_output(model_output, v_ego, lat_action_t, self.generation) + if v_ego > self.MIN_LAT_CONTROL_SPEED: + desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, self.LAT_SMOOTH_SECONDS) + else: + desired_curvature = prev_action.desiredCurvature + + return log.ModelDataV2.Action(desiredCurvature=float(desired_curvature),desiredAcceleration=float(desired_accel), shouldStop=bool(should_stop)) + def main(demo=False): cloudlog.warning("modeld init") @@ -170,7 +219,7 @@ def main(demo=False): params = Params() # setup filter to track dropped frames - frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_FREQ) + frame_dropped_filter = FirstOrderFilter(0., 10., 1. / model.constants.MODEL_FREQ) frame_id = 0 last_vipc_frame_id = 0 run_count = 0 @@ -189,8 +238,9 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) - # Enable lagd support for modeld_v2 - steer_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS + # TODO Move smooth seconds to action function + long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS + prev_action = log.ModelDataV2.Action() DH = DesireHelper() @@ -220,7 +270,7 @@ def main(demo=False): if abs(meta_main.timestamp_sof - meta_extra.timestamp_sof) > 10000000: cloudlog.error(f"frames out of sync! main: {meta_main.frame_id} ({meta_main.timestamp_sof / 1e9:.5f}),\ - extra: {meta_extra.frame_id} ({meta_extra.timestamp_sof / 1e9:.5f})") + extra: {meta_extra.frame_id} ({meta_extra.timestamp_sof / 1e9:.5f})") else: # Use single camera @@ -232,18 +282,20 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) + steer_delay = sm["liveDelay"].lateralDelay + model.LAT_SMOOTH_SECONDS if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] - model_transform_main = get_warp_matrix(device_from_calib_euler, dc.ecam.intrinsics if main_wide_camera else dc.fcam.intrinsics, False).astype(np.float32) + model_transform_main = get_warp_matrix(device_from_calib_euler, dc.ecam.intrinsics if main_wide_camera else dc.fcam.intrinsics, + False).astype(np.float32) model_transform_extra = get_warp_matrix(device_from_calib_euler, dc.ecam.intrinsics, True).astype(np.float32) live_calib_seen = True traffic_convention = np.zeros(2) traffic_convention[int(is_rhd)] = 1 - vec_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) - if desire >= 0 and desire < ModelConstants.DESIRE_LEN: + vec_desire = np.zeros(model.constants.DESIRE_LEN, dtype=np.float32) + if desire >= 0 and desire < model.constants.DESIRE_LEN: vec_desire[desire] = 1 # tracked dropped frames @@ -276,7 +328,10 @@ def main(demo=False): modelv2_send = messaging.new_message('modelV2') drivingdata_send = messaging.new_message('drivingModelData') posenet_send = messaging.new_message('cameraOdometry') - fill_model_msg(drivingdata_send, modelv2_send, model_output, v_ego, steer_delay, + + action = model.get_action_from_model(model_output, prev_action, steer_delay + DT_MDL, long_delay + DT_MDL, v_ego) + prev_action = action + fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, load_meta_constants()) diff --git a/sunnypilot/modeld_v2/parse_model_outputs_split.py b/sunnypilot/modeld_v2/parse_model_outputs_split.py new file mode 100644 index 0000000000..7fab66e038 --- /dev/null +++ b/sunnypilot/modeld_v2/parse_model_outputs_split.py @@ -0,0 +1,127 @@ +import numpy as np +from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants + + +def safe_exp(x, out=None): + # -11 is around 10**14, more causes float16 overflow + return np.exp(np.clip(x, -np.inf, 11), out=out) + + +def sigmoid(x): + return 1. / (1. + safe_exp(-x)) + + +def softmax(x, axis=-1): + x -= np.max(x, axis=axis, keepdims=True) + if x.dtype == np.float32 or x.dtype == np.float64: + safe_exp(x, out=x) + else: + x = safe_exp(x) + x /= np.sum(x, axis=axis, keepdims=True) + return x + + +class Parser: + def __init__(self, ignore_missing=False): + self.ignore_missing = ignore_missing + + def check_missing(self, outs, name): + if name not in outs and not self.ignore_missing: + raise ValueError(f"Missing output {name}") + return name not in outs + + def parse_categorical_crossentropy(self, name, outs, out_shape=None): + if self.check_missing(outs, name): + return + raw = outs[name] + if out_shape is not None: + raw = raw.reshape((raw.shape[0],) + out_shape) + outs[name] = softmax(raw, axis=-1) + + def parse_binary_crossentropy(self, name, outs): + if self.check_missing(outs, name): + return + raw = outs[name] + outs[name] = sigmoid(raw) + + def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None): + if self.check_missing(outs, name): + return + raw = outs[name] + raw = raw.reshape((raw.shape[0], max(in_N, 1), -1)) + + n_values = (raw.shape[2] - out_N)//2 + pred_mu = raw[:,:,:n_values] + pred_std = safe_exp(raw[:,:,n_values: 2*n_values]) + + if in_N > 1: + weights = np.zeros((raw.shape[0], in_N, out_N), dtype=raw.dtype) + for i in range(out_N): + weights[:,:,i - out_N] = softmax(raw[:,:,i - out_N], axis=-1) + + if out_N == 1: + for fidx in range(weights.shape[0]): + idxs = np.argsort(weights[fidx][:,0])[::-1] + weights[fidx] = weights[fidx][idxs] + pred_mu[fidx] = pred_mu[fidx][idxs] + pred_std[fidx] = pred_std[fidx][idxs] + full_shape = tuple([raw.shape[0], in_N] + list(out_shape)) + outs[name + '_weights'] = weights + outs[name + '_hypotheses'] = pred_mu.reshape(full_shape) + outs[name + '_stds_hypotheses'] = pred_std.reshape(full_shape) + + pred_mu_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype) + pred_std_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype) + for fidx in range(weights.shape[0]): + for hidx in range(out_N): + idxs = np.argsort(weights[fidx,:,hidx])[::-1] + pred_mu_final[fidx, hidx] = pred_mu[fidx, idxs[0]] + pred_std_final[fidx, hidx] = pred_std[fidx, idxs[0]] + else: + pred_mu_final = pred_mu + pred_std_final = pred_std + + if out_N > 1: + final_shape = tuple([raw.shape[0], out_N] + list(out_shape)) + else: + final_shape = tuple([raw.shape[0],] + list(out_shape)) + outs[name] = pred_mu_final.reshape(final_shape) + outs[name + '_stds'] = pred_std_final.reshape(final_shape) + + def split_outputs(self, outs: dict[str, np.ndarray]) -> None: + if 'lane_lines' in outs: + self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, + out_shape=(SplitModelConstants.NUM_LANE_LINES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('road_edges', outs, in_N=0, out_N=0, + out_shape=(SplitModelConstants.NUM_ROAD_EDGES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH)) + self.parse_mdn('lead', outs, in_N=SplitModelConstants.LEAD_MHP_N, out_N=SplitModelConstants.LEAD_MHP_SELECTION, + out_shape=(SplitModelConstants.LEAD_TRAJ_LEN,SplitModelConstants.LEAD_WIDTH)) + if 'sim_pose' in outs: + self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,)) + for k in ['lead_prob', 'lane_lines_prob']: + self.parse_binary_crossentropy(k, outs) + + def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: + self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,)) + self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.WIDE_FROM_DEVICE_WIDTH,)) + self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,)) + self.split_outputs(outs) + self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(SplitModelConstants.DESIRE_PRED_LEN,SplitModelConstants.DESIRE_PRED_WIDTH)) + self.parse_binary_crossentropy('meta', outs) + return outs + + def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: + self.parse_mdn('plan', outs, in_N=SplitModelConstants.PLAN_MHP_N, out_N=SplitModelConstants.PLAN_MHP_SELECTION, + out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.PLAN_WIDTH)) + self.split_outputs(outs) + if 'lat_planner_solution' in outs: + self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) + if 'desired_curvature' in outs: + self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.DESIRED_CURV_WIDTH,)) + self.parse_categorical_crossentropy('desire_state', outs, out_shape=(SplitModelConstants.DESIRE_PRED_WIDTH,)) + return outs + + def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: + outs = self.parse_vision_outputs(outs) + outs = self.parse_policy_outputs(outs) + return outs diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py index 7076cf1861..27fac08673 100644 --- a/sunnypilot/models/fetcher.py +++ b/sunnypilot/models/fetcher.py @@ -43,6 +43,16 @@ class ModelParser: model.metadata = ModelParser._parse_artifact(metadata) return model + @staticmethod + def _parse_overrides(overrides_data: dict[str, str]) -> list[custom.ModelManagerSP.Override]: + overrides = [] + for key, value in overrides_data.items(): + override = custom.ModelManagerSP.Override() + override.key = key + override.value = value + overrides.append(override) + return overrides + @staticmethod def _parse_bundle(bundle) -> custom.ModelManagerSP.ModelBundle: model_bundle = custom.ModelManagerSP.ModelBundle() @@ -56,6 +66,7 @@ class ModelParser: model_bundle.runner = bundle.get("runner", custom.ModelManagerSP.Runner.snpe) model_bundle.is20hz = bundle.get("is_20hz", False) model_bundle.minimumSelectorVersion = int(bundle["minimum_selector_version"]) + model_bundle.overrides = ModelParser._parse_overrides(bundle.get("overrides", {})) return model_bundle @@ -149,8 +160,9 @@ if __name__ == "__main__": bundles = model_fetcher.get_available_bundles() for bundle in bundles: for model in bundle.models: + model_overrides = {override.key: override.value for override in bundle.overrides} # Print model details - print(f"Bundle: {bundle.internalName}, Type: {model.type}, Status: {bundle.status}") + print(f"Bundle: {bundle.internalName}, Type: {model.type}, Status: {bundle.status}, Overrides: {model_overrides}") # Print artifact details print(f"Artifact: {model.artifact.fileName}, Download URI: {model.artifact.downloadUri.uri}") # Print metadata details diff --git a/sunnypilot/models/helpers.py b/sunnypilot/models/helpers.py index e6001f2642..344a5179c9 100644 --- a/sunnypilot/models/helpers.py +++ b/sunnypilot/models/helpers.py @@ -19,7 +19,7 @@ from openpilot.system.hardware import PC from openpilot.system.hardware.hw import Paths from pathlib import Path -CURRENT_SELECTOR_VERSION = 3 +CURRENT_SELECTOR_VERSION = 4 REQUIRED_MIN_SELECTOR_VERSION = 2 USE_ONNX = os.getenv('USE_ONNX', PC) diff --git a/sunnypilot/models/runners/constants.py b/sunnypilot/models/runners/constants.py new file mode 100644 index 0000000000..cbd1fdb376 --- /dev/null +++ b/sunnypilot/models/runners/constants.py @@ -0,0 +1,18 @@ +import os +import numpy as np +from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLMem +from openpilot.system.hardware.hw import Paths +from cereal import custom + +# Type definitions for clarity +NumpyDict = dict[str, np.ndarray] +ShapeDict = dict[str, tuple[int, ...]] +SliceDict = dict[str, slice] +CLMemDict = dict[str, CLMem] +FrameDict = dict[str, DrivingModelFrame] + +ModelType = custom.ModelManagerSP.Model.Type +Model = custom.ModelManagerSP.Model + +SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') +CUSTOM_MODEL_PATH = Paths.model_root() diff --git a/sunnypilot/models/runners/helpers.py b/sunnypilot/models/runners/helpers.py new file mode 100644 index 0000000000..6a128b340e --- /dev/null +++ b/sunnypilot/models/runners/helpers.py @@ -0,0 +1,35 @@ +from openpilot.sunnypilot.models.helpers import get_active_bundle +from openpilot.sunnypilot.models.runners.model_runner import ModelRunner +from openpilot.sunnypilot.models.runners.tinygrad.tinygrad_runner import TinygradRunner, TinygradSplitRunner +from openpilot.sunnypilot.models.runners.constants import ModelType +from openpilot.system.hardware import TICI + +if not TICI: + from openpilot.sunnypilot.models.runners.onnx.onnx_runner import ONNXRunner + +def get_model_runner() -> ModelRunner: + """ + Factory function to create and return the appropriate ModelRunner instance. + + Selects between ONNXRunner (for non-TICI platforms) and TinygradRunner + (for TICI platforms), choosing TinygradSplitRunner if separate vision/policy + models are detected in the active bundle. + + :return: An instance of a ModelRunner subclass (ONNXRunner, TinygradRunner, or TinygradSplitRunner). + """ + if not TICI: + return ONNXRunner() + + # On TICI platforms, use Tinygrad runners + bundle = get_active_bundle() + if bundle and bundle.models: + model_types = {m.type.raw for m in bundle.models} + # Check if the bundle uses separate vision and policy models + if ModelType.vision in model_types or ModelType.policy in model_types: + return TinygradSplitRunner() + # Otherwise, assume a single model (likely supercombo) + if bundle.models: + return TinygradRunner(bundle.models[0].type.raw) + + # Default fallback to TinygradRunner with the supercombo type if bundle info is missing/incomplete + return TinygradRunner(ModelType.supercombo) diff --git a/sunnypilot/models/runners/model_runner.py b/sunnypilot/models/runners/model_runner.py new file mode 100644 index 0000000000..c210d5b87e --- /dev/null +++ b/sunnypilot/models/runners/model_runner.py @@ -0,0 +1,176 @@ +import os +from abc import abstractmethod, ABC + +import numpy as np +from openpilot.sunnypilot.models.helpers import get_active_bundle +from openpilot.system.hardware import TICI +from openpilot.sunnypilot.models.runners.constants import NumpyDict, ShapeDict, CLMemDict, FrameDict, Model, SliceDict, SEND_RAW_PRED +from openpilot.system.hardware.hw import Paths +import pickle + +CUSTOM_MODEL_PATH = Paths.model_root() + + +# Set QCOM environment variable for TICI devices, potentially enabling hardware acceleration +if TICI: + os.environ['QCOM'] = '1' + + +class ModelData: + """ + Stores metadata and configuration for a specific machine learning model. + + This class loads model metadata (like input shapes and output slices) + from a pickle file associated with a model instance. + + :param model: The machine learning model object containing metadata. + """ + def __init__(self, model: Model): + self.model = model + self.metadata = model.metadata + self.input_shapes: ShapeDict = {} + self.output_slices: SliceDict = {} + if self.metadata: + self._load_metadata() + + def _load_metadata(self) -> None: + """Loads input shapes and output slices from the model's metadata pickle file.""" + metadata_path = f"{CUSTOM_MODEL_PATH}/{self.metadata.fileName}" + with open(metadata_path, 'rb') as f: + model_metadata = pickle.load(f) + self.input_shapes = model_metadata.get('input_shapes', {}) + self.output_slices = model_metadata.get('output_slices', {}) + + +class ModularRunner(ABC): + """ + Represents a modular runner for handling and slicing model outputs. + + This abstract base class is designed to provide an interface for modular + parsing and processing of model outputs. Classes inheriting from it must + implement the specified abstract methods, defining how model outputs + should be handled and stored. The primary goal is to enable structured + parsing of outputs through a dictionary-based method mapping. + + :ivar parser_method_dict: Mapping dictionary containing parser methods + for handling specific types of outputs. + :type parser_method_dict: dict + """ + + @property + @abstractmethod + def parser_method_dict(self) -> dict: + pass + + @parser_method_dict.setter + @abstractmethod + def parser_method_dict(self, value: dict) -> None: + pass + + @abstractmethod + def _slice_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + pass + + +class ModelRunner(ModularRunner): + """ + Abstract base class for managing and executing machine learning models. + + Provides a common interface for loading models, preparing inputs, running + inference, and slicing/parsing outputs based on model metadata. Derived + classes implement the specifics of input preparation and model execution + for different frameworks (e.g., Tinygrad, ONNX). + """ + + def __init__(self): + """Initializes the model runner, loading the active model bundle.""" + self.is_20hz: bool | None = None + self.is_20hz_3d: bool | None = None + self.models: dict[int, ModelData] = {} + self._model_data: ModelData | None = None # Active model data for current operation + self._parser_method_dict: dict = {} + self.inputs: dict = {} + self._parser = None + self._load_models() + self._constants = None + + @property + def constants(self): + return self._constants + + @property + def parser_method_dict(self) -> dict: + """Returns the dictionary mapping model types to their respective parsing methods.""" + return self._parser_method_dict + + @parser_method_dict.setter + def parser_method_dict(self, value: dict) -> None: + """Sets the dictionary mapping model types to their respective parsing methods.""" + self._parser_method_dict = value + + def _load_models(self) -> None: + """Loads the active model bundle configuration and sets up ModelData.""" + bundle = get_active_bundle() + if not bundle: + raise ValueError("No active model bundle found, why are we being executed?") + + self.models = {model.type.raw: ModelData(model) for model in bundle.models} + self.is_20hz = bundle.is20hz + self.is_20hz_3d = False + + @property + def input_shapes(self) -> ShapeDict: + """Returns the input shapes for the currently active model.""" + if self._model_data: + return self._model_data.input_shapes + raise ValueError("Model data is not available. Ensure the model is loaded correctly.") + + @property + def output_slices(self) -> SliceDict: + """Returns the output slices for the currently active model.""" + if self._model_data: + return self._model_data.output_slices + raise ValueError("Model data is not available. Ensure the model is loaded correctly.") + + @abstractmethod + def prepare_inputs(self, imgs_cl: CLMemDict, numpy_inputs: NumpyDict, frames: FrameDict) -> dict: + """ + Abstract method to prepare inputs for model inference. + + :param imgs_cl: Dictionary of OpenCL memory objects for image inputs. + :param numpy_inputs: Dictionary of numpy arrays for non-image inputs. + :param frames: Dictionary of DrivingModelFrame objects for context. + :return: Dictionary of prepared inputs ready for the model. + """ + raise NotImplementedError + + @abstractmethod + def _run_model(self) -> NumpyDict: + """ + Abstract method to execute model inference with prepared inputs. + + :return: Dictionary containing the model's raw output arrays. + """ + raise NotImplementedError + + def _slice_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + """ + Slices the raw model output array based on the output_slices metadata. + + :param model_outputs: The raw numpy array output from the model. + :return: A dictionary where keys are output names and values are sliced numpy arrays. + """ + if not self._model_data: + raise ValueError("Model data is not available. Ensure the model is loaded correctly.") + sliced_outputs = {k: model_outputs[np.newaxis, v] for k, v in self._model_data.output_slices.items()} + if SEND_RAW_PRED: + sliced_outputs['raw_pred'] = model_outputs.copy() # Optionally include the full raw output + return sliced_outputs + + def run_model(self) -> NumpyDict: + """ + Executes the model inference pipeline: runs the model and parses outputs. + + :return: Dictionary containing the final parsed model outputs. + """ + return self._run_model() # Parsing is handled within specific runner implementations diff --git a/sunnypilot/models/runners/onnx/onnx_runner.py b/sunnypilot/models/runners/onnx/onnx_runner.py new file mode 100644 index 0000000000..1ffead4560 --- /dev/null +++ b/sunnypilot/models/runners/onnx/onnx_runner.py @@ -0,0 +1,62 @@ +import numpy as np + +from openpilot.sunnypilot.modeld_v2 import MODEL_PATH +from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES +from openpilot.sunnypilot.models.runners.constants import ModelType, ShapeDict, CLMemDict, NumpyDict, FrameDict +from openpilot.sunnypilot.models.runners.model_runner import ModelRunner +from openpilot.sunnypilot.modeld_v2.constants import ModelConstants + + +class ONNXRunner(ModelRunner): + """ + A ModelRunner implementation for executing ONNX models using ONNX Runtime CPU. + + Handles loading the ONNX model, preparing inputs as numpy arrays, running + inference, and parsing outputs. This runner is typically used on non-TICI platforms. + """ + def __init__(self): + super().__init__() + # Initialize ONNX Runtime session for the model at MODEL_PATH + self.runner = make_onnx_cpu_runner(MODEL_PATH) + # Map expected input names to numpy dtypes + self.input_to_nptype = { + model_input.name: ORT_TYPES_TO_NP_TYPES[model_input.type] + for model_input in self.runner.get_inputs() + } + # For ONNX, _model_data isn't strictly necessary as shapes/types come from the runner + # However, we might still need output_slices if custom models define them. + # We assume supercombo type for potentially loading output_slices metadata if available. + self._model_data = self.models.get(ModelType.supercombo) + self._constants = ModelConstants # Constants for ONNX models, if needed + + @property + def input_shapes(self) -> ShapeDict: + """Returns the input shapes defined in the ONNX model.""" + # ONNX shapes are derived directly from the model definition via the runner + return {runner_input.name: runner_input.shape for runner_input in self.runner.get_inputs()} + + def prepare_inputs(self, imgs_cl: CLMemDict, numpy_inputs: NumpyDict, frames: FrameDict) -> dict: + """Prepares inputs for the ONNX model as numpy arrays.""" + self.inputs = numpy_inputs # Start with non-image numpy inputs + # Convert image inputs from OpenCL buffers to numpy arrays + for key in imgs_cl: + buffer = frames[key].buffer_from_cl(imgs_cl[key]) + reshaped_buffer = buffer.reshape(self.input_shapes[key]) + self.inputs[key] = reshaped_buffer.astype(dtype=self.input_to_nptype[key]) + return self.inputs + + def _parse_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + """Parses the raw ONNX model outputs using the standard Parser.""" + # Use slicing if metadata is available, otherwise pass raw outputs + if self._model_data is None: + raise ValueError("Model data is not available. Ensure the model is loaded correctly.") + + outputs_to_parse = self._slice_outputs(model_outputs) if self._model_data else {'raw_pred': model_outputs} + result: NumpyDict = self.parser_method_dict[self._model_data.model.type.raw](outputs_to_parse) + return result + + def _run_model(self) -> NumpyDict: + """Runs the ONNX model inference and parses the outputs.""" + # Execute the ONNX Runtime session + outputs = self.runner.run(None, self.inputs)[0].flatten() + return self._parse_outputs(outputs) diff --git a/sunnypilot/models/runners/tinygrad/model_types.py b/sunnypilot/models/runners/tinygrad/model_types.py new file mode 100644 index 0000000000..ba388aed93 --- /dev/null +++ b/sunnypilot/models/runners/tinygrad/model_types.py @@ -0,0 +1,59 @@ +import os +from abc import ABC + +import numpy as np +from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser as CombinedParser +from openpilot.sunnypilot.modeld_v2.parse_model_outputs_split import Parser as SplitParser +from openpilot.sunnypilot.models.runners.constants import ModelType, NumpyDict +from openpilot.sunnypilot.models.runners.model_runner import ModularRunner +from openpilot.system.hardware.hw import Paths + + +SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') +CUSTOM_MODEL_PATH = Paths.model_root() + + +class PolicyTinygrad(ModularRunner, ABC): + """ + A TinygradRunner specialized for policy-only models. + + Uses a SplitParser to handle outputs specific to the policy part of a split model setup. + """ + def __init__(self): + self._policy_parser = SplitParser() + self.parser_method_dict[ModelType.policy] = self._parse_policy_outputs + + def _parse_policy_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + """Parses policy model outputs using SplitParser.""" + result: NumpyDict = self._policy_parser.parse_policy_outputs(self._slice_outputs(model_outputs)) + return result + +class VisionTinygrad(ModularRunner, ABC): + """ + A TinygradRunner specialized for vision-only models. + + Uses a SplitParser to handle outputs specific to the vision part of a split model setup. + """ + def __init__(self): + self._vision_parser = SplitParser() + self.parser_method_dict[ModelType.vision] = self._parse_vision_outputs + + def _parse_vision_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + """Parses vision model outputs using SplitParser.""" + result: NumpyDict = self._vision_parser.parse_vision_outputs(self._slice_outputs(model_outputs)) + return result + +class SupercomboTinygrad(ModularRunner, ABC): + """ + A TinygradRunner specialized for vision-only models. + + Uses a SplitParser to handle outputs specific to the vision part of a split model setup. + """ + def __init__(self): + self._supercombo_parser = CombinedParser() + self.parser_method_dict[ModelType.supercombo] = self._parse_supercombo_outputs + + def _parse_supercombo_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + """Parses vision model outputs using SplitParser.""" + result: NumpyDict = self._supercombo_parser.parse_outputs(self._slice_outputs(model_outputs)) + return result diff --git a/sunnypilot/models/runners/tinygrad/tinygrad_runner.py b/sunnypilot/models/runners/tinygrad/tinygrad_runner.py new file mode 100644 index 0000000000..2e4fd45294 --- /dev/null +++ b/sunnypilot/models/runners/tinygrad/tinygrad_runner.py @@ -0,0 +1,129 @@ +import pickle + +import numpy as np +from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address +from openpilot.sunnypilot.models.runners.constants import CLMemDict, FrameDict, NumpyDict, ModelType, ShapeDict, CUSTOM_MODEL_PATH, SliceDict +from openpilot.sunnypilot.models.runners.model_runner import ModelRunner +from openpilot.sunnypilot.models.runners.tinygrad.model_types import PolicyTinygrad, VisionTinygrad, SupercomboTinygrad +from openpilot.system.hardware import TICI +from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants +from openpilot.sunnypilot.modeld_v2.constants import ModelConstants + +from tinygrad.tensor import Tensor + + +class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTinygrad): + """ + A ModelRunner implementation for executing Tinygrad models. + + Handles loading Tinygrad model artifacts (.pkl), preparing inputs as Tinygrad + Tensors (potentially using QCOM extensions on TICI), running inference, + and parsing the outputs. + + :param model_type: The type of model (e.g., supercombo) to load and run. + """ + def __init__(self, model_type: int = ModelType.supercombo): + ModelRunner.__init__(self) + SupercomboTinygrad.__init__(self) + PolicyTinygrad.__init__(self) + VisionTinygrad.__init__(self) + self._constants = ModelConstants + self._model_data = self.models.get(model_type) + if not self._model_data or not self._model_data.model: + raise ValueError(f"Model data for type {model_type} not available.") + + artifact_filename = self._model_data.model.artifact.fileName + assert artifact_filename.endswith('_tinygrad.pkl'), \ + f"Invalid model file {artifact_filename} for TinygradRunner" + + model_pkl_path = f"{CUSTOM_MODEL_PATH}/{artifact_filename}" + with open(model_pkl_path, "rb") as f: + try: + # Load the compiled Tinygrad model runner function + self.model_run = pickle.load(f) + except FileNotFoundError as e: + # Provide a helpful error message if the model was built for a different platform + assert "/dev/kgsl-3d0" not in str(e), "Model was built on C3 or C3X, but is being loaded on PC" + raise + + # Map input names to their required dtype and device from the loaded model + self.input_to_dtype = {} + self.input_to_device = {} + for idx, name in enumerate(self.model_run.captured.expected_names): + info = self.model_run.captured.expected_st_vars_dtype_device[idx] + self.input_to_dtype[name] = info[2] # dtype + self.input_to_device[name] = info[3] # device + + def prepare_vision_inputs(self, imgs_cl: CLMemDict, frames: FrameDict): + """Prepares vision (image) inputs as Tinygrad Tensors.""" + for key in imgs_cl: + if TICI and key not in self.inputs: + # On TICI, directly use OpenCL memory address for efficiency via QCOM extensions + self.inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=self.input_to_dtype[key]) + elif not TICI: + # On other platforms, copy data from CL buffer to a numpy array first + shape = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]) + self.inputs[key] = Tensor(shape, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize() + + def prepare_policy_inputs(self, numpy_inputs: NumpyDict): + """Prepares non-image (policy) inputs as Tinygrad Tensors.""" + for key, value in numpy_inputs.items(): + self.inputs[key] = Tensor(value, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize() + + def prepare_inputs(self, imgs_cl: CLMemDict, numpy_inputs: NumpyDict, frames: FrameDict) -> dict: + """Prepares all vision and policy inputs for the model.""" + self.prepare_vision_inputs(imgs_cl, frames) + self.prepare_policy_inputs(numpy_inputs) + return self.inputs + + def _run_model(self) -> NumpyDict: + """Runs the Tinygrad model inference and parses the outputs.""" + outputs = self.model_run(**self.inputs).numpy().flatten() + return self._parse_outputs(outputs) + + def _parse_outputs(self, model_outputs: np.ndarray) -> NumpyDict: + """Parses the raw model outputs using the standard Parser.""" + if self._model_data is None: + raise ValueError("Model data is not available. Ensure the model is loaded correctly.") + + result: NumpyDict = self.parser_method_dict[self._model_data.model.type.raw](model_outputs) + return result + + +class TinygradSplitRunner(ModelRunner): + """ + A ModelRunner that coordinates separate TinygradVisionRunner and TinygradPolicyRunner instances. + + Manages the execution of split vision and policy models, combining their inputs and outputs. + """ + def __init__(self): + super().__init__() + self.is_20hz_3d = True + self.vision_runner = TinygradRunner(ModelType.vision) + self.policy_runner = TinygradRunner(ModelType.policy) + self._constants = SplitModelConstants + + def _run_model(self) -> NumpyDict: + """Runs both vision and policy models and merges their parsed outputs.""" + policy_output = self.policy_runner.run_model() + vision_output = self.vision_runner.run_model() + return {**policy_output, **vision_output} # Combine results + + @property + def input_shapes(self) -> ShapeDict: + """Returns the combined input shapes from both vision and policy models.""" + return {**self.policy_runner.input_shapes, **self.vision_runner.input_shapes} + + @property + def output_slices(self) -> SliceDict: + """Returns the combined output slices from both vision and policy models.""" + return {**self.policy_runner.output_slices, **self.vision_runner.output_slices} + + def prepare_inputs(self, imgs_cl: CLMemDict, numpy_inputs: NumpyDict, frames: FrameDict) -> dict: + """Prepares inputs for both vision and policy models.""" + # Policy inputs only depend on numpy_inputs + self.policy_runner.prepare_policy_inputs(numpy_inputs) + # Vision inputs depend on imgs_cl and frames + self.vision_runner.prepare_vision_inputs(imgs_cl, frames) + # Return combined inputs (though they are stored within respective runners) + return {**self.policy_runner.inputs, **self.vision_runner.inputs} diff --git a/sunnypilot/models/split_model_constants.py b/sunnypilot/models/split_model_constants.py new file mode 100644 index 0000000000..a3e1dce8f6 --- /dev/null +++ b/sunnypilot/models/split_model_constants.py @@ -0,0 +1,94 @@ +import numpy as np + + +def index_function(idx, max_val=192, max_idx=32): + return max_val * ((idx/max_idx)**2) + + +class SplitModelConstants: + # time and distance indices + IDX_N = 33 + T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)] + X_IDXS = [index_function(idx, max_val=192.0) for idx in range(IDX_N)] + LEAD_T_IDXS = [0., 2., 4., 6., 8., 10.] + LEAD_T_OFFSETS = [0., 2., 4.] + META_T_IDXS = [2., 4., 6., 8., 10.] + + # model inputs constants + MODEL_FREQ = 20 + HISTORY_FREQ = 5 + HISTORY_LEN_SECONDS = 5 + TEMPORAL_SKIP = MODEL_FREQ // HISTORY_FREQ + FULL_HISTORY_BUFFER_LEN = MODEL_FREQ * HISTORY_LEN_SECONDS + INPUT_HISTORY_BUFFER_LEN = HISTORY_FREQ * HISTORY_LEN_SECONDS + + FEATURE_LEN = 512 + + DESIRE_LEN = 8 + TRAFFIC_CONVENTION_LEN = 2 + LAT_PLANNER_STATE_LEN = 4 + LATERAL_CONTROL_PARAMS_LEN = 2 + PREV_DESIRED_CURV_LEN = 1 + + # model outputs constants + FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32) + FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32) + FCW_5MS2_PROBS_WIDTH = 5 + FCW_3MS2_PROBS_WIDTH = 2 + + DISENGAGE_WIDTH = 5 + POSE_WIDTH = 6 + WIDE_FROM_DEVICE_WIDTH = 3 + LEAD_WIDTH = 4 + LANE_LINES_WIDTH = 2 + ROAD_EDGES_WIDTH = 2 + PLAN_WIDTH = 15 + DESIRE_PRED_WIDTH = 8 + LAT_PLANNER_SOLUTION_WIDTH = 4 + DESIRED_CURV_WIDTH = 1 + + NUM_LANE_LINES = 4 + NUM_ROAD_EDGES = 2 + + LEAD_TRAJ_LEN = 6 + DESIRE_PRED_LEN = 4 + + PLAN_MHP_N = 5 + LEAD_MHP_N = 2 + PLAN_MHP_SELECTION = 1 + LEAD_MHP_SELECTION = 3 + + FCW_THRESHOLD_5MS2_HIGH = 0.15 + FCW_THRESHOLD_5MS2_LOW = 0.05 + FCW_THRESHOLD_3MS2 = 0.7 + + CONFIDENCE_BUFFER_LEN = 5 + RYG_GREEN = 0.01165 + RYG_YELLOW = 0.06157 + + POLY_PATH_DEGREE = 4 + + +# model outputs slices +class Plan: + POSITION = slice(0, 3) + VELOCITY = slice(3, 6) + ACCELERATION = slice(6, 9) + T_FROM_CURRENT_EULER = slice(9, 12) + ORIENTATION_RATE = slice(12, 15) + + +class Meta: + ENGAGED = slice(0, 1) + # next 2, 4, 6, 8, 10 seconds + GAS_DISENGAGE = slice(1, 31, 6) + BRAKE_DISENGAGE = slice(2, 31, 6) + STEER_OVERRIDE = slice(3, 31, 6) + HARD_BRAKE_3 = slice(4, 31, 6) + HARD_BRAKE_4 = slice(5, 31, 6) + HARD_BRAKE_5 = slice(6, 31, 6) + # next 0, 2, 4, 6, 8, 10 seconds + GAS_PRESS = slice(31, 55, 4) + BRAKE_PRESS = slice(32, 55, 4) + LEFT_BLINKER = slice(33, 55, 4) + RIGHT_BLINKER = slice(34, 55, 4) diff --git a/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py b/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py index 9f0c503fb4..56f32373d5 100644 --- a/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py +++ b/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py @@ -7,7 +7,6 @@ See the LICENSE.md file in the root directory for more details. from cereal import messaging, custom from opendbc.car import structs -from openpilot.sunnypilot.models.helpers import get_active_model_runner from openpilot.sunnypilot.selfdrive.controls.lib.dec.dec import DynamicExperimentalController DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimentalControlState @@ -16,7 +15,6 @@ DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimen class LongitudinalPlannerSP: def __init__(self, CP: structs.CarParams, mpc): self.dec = DynamicExperimentalController(CP, mpc) - self.is_stock = get_active_model_runner() == custom.ModelManagerSP.Runner.stock def get_mpc_mode(self) -> str | None: if not self.dec.active(): From 840ced5005eefacde09976555d0226155f849cb9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 25 May 2025 23:41:03 +0800 Subject: [PATCH 330/477] system/ui: add EGL support to CameraView (#35338) * add EGL support to CameraView * view 3 cameras * use a more direct approach * add new line * cleanup * cleanup close() * extract EGL to a seperate file * cleanup * add try/except to close() * rename egl_textures * improve implementation --- system/ui/lib/egl.py | 177 ++++++++++++++++++++++++++++++++ system/ui/widgets/cameraview.py | 152 ++++++++++++++++++++++----- 2 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 system/ui/lib/egl.py diff --git a/system/ui/lib/egl.py b/system/ui/lib/egl.py new file mode 100644 index 0000000000..d43be482b3 --- /dev/null +++ b/system/ui/lib/egl.py @@ -0,0 +1,177 @@ +import os +import cffi +from dataclasses import dataclass +from typing import Any +from openpilot.common.swaglog import cloudlog + + +# EGL constants +EGL_LINUX_DMA_BUF_EXT = 0x3270 +EGL_WIDTH = 0x3057 +EGL_HEIGHT = 0x3056 +EGL_LINUX_DRM_FOURCC_EXT = 0x3271 +EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272 +EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273 +EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274 +EGL_DMA_BUF_PLANE1_FD_EXT = 0x3275 +EGL_DMA_BUF_PLANE1_OFFSET_EXT = 0x3276 +EGL_DMA_BUF_PLANE1_PITCH_EXT = 0x3277 +EGL_NONE = 0x3038 +GL_TEXTURE0 = 0x84C0 +GL_TEXTURE_EXTERNAL_OES = 0x8D65 + +# DRM Format for NV12 +DRM_FORMAT_NV12 = 842094158 + +@dataclass +class EGLImage: + """Container for EGL image and associated resources""" + + egl_image: Any + fd: int + + +@dataclass +class EGLState: + """Container for all EGL-related state""" + + initialized: bool = False + ffi: Any = None + egl_lib: Any = None + gles_lib: Any = None + + # EGL display connection - shared across all users + display: Any = None + + # Constants + NO_CONTEXT: Any = None + NO_DISPLAY: Any = None + NO_IMAGE_KHR: Any = None + + # Function pointers + get_current_display: Any = None + create_image_khr: Any = None + destroy_image_khr: Any = None + image_target_texture: Any = None + get_error: Any = None + bind_texture: Any = None + active_texture: Any = None + + +# Create a single instance of the state +_egl = EGLState() + + +def init_egl() -> bool: + """Initialize EGL and load necessary functions""" + global _egl + + # Don't re-initialize if already done + if _egl.initialized: + return True + + try: + _egl.ffi = cffi.FFI() + _egl.ffi.cdef(""" + typedef int EGLint; + typedef unsigned int EGLBoolean; + typedef unsigned int EGLenum; + typedef unsigned int GLenum; + typedef void *EGLContext; + typedef void *EGLDisplay; + typedef void *EGLClientBuffer; + typedef void *EGLImageKHR; + typedef void *GLeglImageOES; + + EGLDisplay eglGetCurrentDisplay(void); + EGLint eglGetError(void); + EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, + EGLenum target, EGLClientBuffer buffer, + const EGLint *attrib_list); + EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR image); + void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image); + void glBindTexture(GLenum target, unsigned int texture); + void glActiveTexture(GLenum texture); + """) + + # Load libraries + _egl.egl_lib = _egl.ffi.dlopen("libEGL.so") + _egl.gles_lib = _egl.ffi.dlopen("libGLESv2.so") + + # Cast NULL pointers + _egl.NO_CONTEXT = _egl.ffi.cast("void *", 0) + _egl.NO_DISPLAY = _egl.ffi.cast("void *", 0) + _egl.NO_IMAGE_KHR = _egl.ffi.cast("void *", 0) + + # Bind functions + _egl.get_current_display = _egl.egl_lib.eglGetCurrentDisplay + _egl.create_image_khr = _egl.egl_lib.eglCreateImageKHR + _egl.destroy_image_khr = _egl.egl_lib.eglDestroyImageKHR + _egl.image_target_texture = _egl.gles_lib.glEGLImageTargetTexture2DOES + _egl.get_error = _egl.egl_lib.eglGetError + _egl.bind_texture = _egl.gles_lib.glBindTexture + _egl.active_texture = _egl.gles_lib.glActiveTexture + + # Initialize EGL display once here + _egl.display = _egl.get_current_display() + if _egl.display == _egl.NO_DISPLAY: + raise RuntimeError("Failed to get EGL display") + + _egl.initialized = True + return True + except Exception as e: + cloudlog.exception(f"EGL initialization failed: {e}") + _egl.initialized = False + return False + + +def create_egl_image(width: int, height: int, stride: int, fd: int, uv_offset: int) -> EGLImage | None: + assert _egl.initialized, "EGL not initialized" + + # Duplicate fd since EGL needs it + dup_fd = os.dup(fd) + + # Create image attributes for EGL + img_attrs = [ + EGL_WIDTH, width, + EGL_HEIGHT, height, + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV12, + EGL_DMA_BUF_PLANE0_FD_EXT, dup_fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, + EGL_DMA_BUF_PLANE0_PITCH_EXT, stride, + EGL_DMA_BUF_PLANE1_FD_EXT, dup_fd, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, uv_offset, + EGL_DMA_BUF_PLANE1_PITCH_EXT, stride, + EGL_NONE + ] + + attr_array = _egl.ffi.new("int[]", img_attrs) + egl_image = _egl.create_image_khr(_egl.display, _egl.NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, _egl.ffi.NULL, attr_array) + + if egl_image == _egl.NO_IMAGE_KHR: + cloudlog.error(f"Failed to create EGL image: {_egl.get_error()}") + os.close(dup_fd) + return None + + return EGLImage(egl_image=egl_image, fd=dup_fd) + + +def destroy_egl_image(egl_image: EGLImage) -> None: + assert _egl.initialized, "EGL not initialized" + + _egl.destroy_image_khr(_egl.display, egl_image.egl_image) + + # Close the duplicated fd we created in create_egl_image() + # We need to handle OSError since the fd might already be closed + try: + os.close(egl_image.fd) + except OSError: + pass + + +def bind_egl_image_to_texture(texture_id: int, egl_image: EGLImage) -> None: + assert _egl.initialized, "EGL not initialized" + + _egl.active_texture(GL_TEXTURE0) + _egl.bind_texture(GL_TEXTURE_EXTERNAL_OES, texture_id) + _egl.image_target_texture(GL_TEXTURE_EXTERNAL_OES, egl_image.egl_image) diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index 09fe306176..01aac0370b 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -1,6 +1,8 @@ import pyray as rl -from msgq.visionipc import VisionIpcClient, VisionStreamType +from openpilot.system.hardware import TICI +from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage VERTEX_SHADER = """ @@ -19,30 +21,67 @@ void main() { } """ -FRAME_FRAGMENT_SHADER = """ -#version 300 es -precision mediump float; -in vec2 fragTexCoord; -uniform sampler2D texture0; -uniform sampler2D texture1; -out vec4 fragColor; -void main() { - float y = texture(texture0, fragTexCoord).r; - vec2 uv = texture(texture1, fragTexCoord).ra - 0.5; - fragColor = vec4(y + 1.402*uv.y, y - 0.344*uv.x - 0.714*uv.y, y + 1.772*uv.x, 1.0); -} -""" +# Choose fragment shader based on platform capabilities +if TICI: + FRAME_FRAGMENT_SHADER = """ + #version 300 es + #extension GL_OES_EGL_image_external_essl3 : enable + precision mediump float; + in vec2 fragTexCoord; + uniform samplerExternalOES texture0; + out vec4 fragColor; + void main() { + vec4 color = texture(texture0, fragTexCoord); + fragColor = vec4(pow(color.rgb, vec3(1.0/1.28)), color.a); + } + """ +else: + FRAME_FRAGMENT_SHADER = """ + #version 300 es + precision mediump float; + in vec2 fragTexCoord; + uniform sampler2D texture0; + uniform sampler2D texture1; + out vec4 fragColor; + void main() { + float y = texture(texture0, fragTexCoord).r; + vec2 uv = texture(texture1, fragTexCoord).ra - 0.5; + fragColor = vec4(y + 1.402*uv.y, y - 0.344*uv.x - 0.714*uv.y, y + 1.772*uv.x, 1.0); + } + """ class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): self.client = VisionIpcClient(name, stream_type, False) self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER) + + self.frame: VisionBuf | None = None self.texture_y: rl.Texture | None = None self.texture_uv: rl.Texture | None = None - self.frame = None - def close(self): + # EGL resources + self.egl_images: dict[int, EGLImage] = {} + self.egl_texture: rl.Texture | None = None + + # Initialize EGL for zero-copy rendering on TICI + if TICI: + if not init_egl(): + raise RuntimeError("Failed to initialize EGL") + + # Create a 1x1 pixel placeholder texture for EGL image binding + temp_image = rl.gen_image_color(1, 1, rl.BLACK) + self.egl_texture = rl.load_texture_from_image(temp_image) + rl.unload_image(temp_image) + + def close(self) -> None: self._clear_textures() + + # Clean up EGL texture + if TICI and self.egl_texture: + rl.unload_texture(self.egl_texture) + self.egl_texture = None + + # Clean up shader if self.shader and self.shader.id: rl.unload_shader(self.shader) @@ -50,17 +89,14 @@ class CameraView: if not self._ensure_connection(): return + # Try to get a new buffer without blocking buffer = self.client.recv(timeout_ms=0) - self.frame = buffer if buffer else self.frame - if not self.frame or not self.texture_y or not self.texture_uv: + if buffer: + self.frame = buffer + + if not self.frame: return - y_data = self.frame.data[: self.frame.uv_offset] - uv_data = self.frame.data[self.frame.uv_offset :] - - rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data)) - rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data)) - # Calculate scaling to maintain aspect ratio scale = min(rect.width / self.frame.width, rect.height / self.frame.height) x_offset = rect.x + (rect.width - (self.frame.width * scale)) / 2 @@ -68,6 +104,53 @@ class CameraView: src_rect = rl.Rectangle(0, 0, float(self.frame.width), float(self.frame.height)) dst_rect = rl.Rectangle(x_offset, y_offset, self.frame.width * scale, self.frame.height * scale) + # Render with appropriate method + if TICI: + self._render_egl(src_rect, dst_rect) + else: + self._render_textures(src_rect, dst_rect) + + def _render_egl(self, src_rect: rl.Rectangle, dst_rect: rl.Rectangle) -> None: + """Render using EGL for direct buffer access""" + if self.frame is None or self.egl_texture is None: + return + + idx = self.frame.idx + egl_image = self.egl_images.get(idx) + + # Create EGL image if needed + if egl_image is None: + egl_image = create_egl_image(self.frame.width, self.frame.height, self.frame.stride, self.frame.fd, self.frame.uv_offset) + if egl_image: + self.egl_images[idx] = egl_image + else: + return + + # Update texture dimensions to match current frame + self.egl_texture.width = self.frame.width + self.egl_texture.height = self.frame.height + + # Bind the EGL image to our texture + bind_egl_image_to_texture(self.egl_texture.id, egl_image) + + # Render with shader + rl.begin_shader_mode(self.shader) + rl.draw_texture_pro(self.egl_texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) + rl.end_shader_mode() + + def _render_textures(self, src_rect: rl.Rectangle, dst_rect: rl.Rectangle) -> None: + """Render using texture copies""" + if not self.texture_y or not self.texture_uv or self.frame is None: + return + + # Update textures with new frame data + y_data = self.frame.data[: self.frame.uv_offset] + uv_data = self.frame.data[self.frame.uv_offset :] + + rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data)) + rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data)) + + # Render with shader rl.begin_shader_mode(self.shader) rl.set_shader_value_texture(self.shader, rl.get_shader_location(self.shader, "texture1"), self.texture_uv) rl.draw_texture_pro(self.texture_y, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) @@ -80,17 +163,30 @@ class CameraView: return False self._clear_textures() - self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride), - int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)) - self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), - int(self.client.height // 2), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)) + + if not TICI: + self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride), + int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)) + self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), + int(self.client.height // 2), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)) + return True def _clear_textures(self): if self.texture_y and self.texture_y.id: rl.unload_texture(self.texture_y) + self.texture_y = None + if self.texture_uv and self.texture_uv.id: rl.unload_texture(self.texture_uv) + self.texture_uv = None + + # Clean up EGL resources + if TICI: + for data in self.egl_images.values(): + destroy_egl_image(data) + self.egl_images = {} + if __name__ == "__main__": gui_app.init_window("watch3") From 993b1b4d88bb55d4ab87ddd2ffc51e8cfc866522 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Sun, 25 May 2025 08:54:07 -0700 Subject: [PATCH 331/477] refactor: LPA interface, CLI location (#35328) * refactor: LPABase, simpler switch() interface * leave this since LPABase hides * hw-agnostic esim.py * newline * use latest --- system/hardware/base.py | 42 ++++++++++ system/hardware/esim.py | 38 +++++++++ system/hardware/pc/hardware.py | 6 +- system/hardware/tici/esim.py | 105 ++++++------------------ system/hardware/tici/hardware.py | 6 +- system/hardware/tici/tests/test_esim.py | 8 +- tools/op.sh | 2 +- 7 files changed, 117 insertions(+), 90 deletions(-) create mode 100755 system/hardware/esim.py mode change 100755 => 100644 system/hardware/tici/esim.py diff --git a/system/hardware/base.py b/system/hardware/base.py index 90b42b2f1f..e429f0e9f2 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -6,6 +6,19 @@ from cereal import log NetworkType = log.DeviceState.NetworkType +class LPAError(RuntimeError): + pass + +class LPAProfileNotFoundError(LPAError): + pass + +@dataclass +class Profile: + iccid: str + nickname: str + enabled: bool + provider: str + @dataclass class ThermalZone: # a zone from /sys/class/thermal/thermal_zone* @@ -51,6 +64,31 @@ class ThermalConfig: ret[f.name + "TempC"] = v.read() return ret +class LPABase(ABC): + @abstractmethod + def list_profiles(self) -> list[Profile]: + pass + + @abstractmethod + def get_active_profile(self) -> Profile | None: + pass + + @abstractmethod + def delete_profile(self, iccid: str) -> None: + pass + + @abstractmethod + def download_profile(self, qr: str, nickname: str | None = None) -> None: + pass + + @abstractmethod + def nickname_profile(self, iccid: str, nickname: str) -> None: + pass + + @abstractmethod + def switch_profile(self, iccid: str) -> None: + pass + class HardwareBase(ABC): @staticmethod def get_cmdline() -> dict[str, str]: @@ -105,6 +143,10 @@ class HardwareBase(ABC): def get_sim_info(self): pass + @abstractmethod + def get_sim_lpa(self) -> LPABase: + pass + @abstractmethod def get_network_strength(self, network_type): pass diff --git a/system/hardware/esim.py b/system/hardware/esim.py new file mode 100755 index 0000000000..6668a1cdd3 --- /dev/null +++ b/system/hardware/esim.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +import argparse + +from openpilot.system.hardware import HARDWARE + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(prog='esim.py', description='manage eSIM profiles on your comma device', epilog='comma.ai') + parser.add_argument('--backend', choices=['qmi', 'at'], default='qmi', help='use the specified backend, defaults to qmi') + parser.add_argument('--switch', metavar='iccid', help='switch to profile') + parser.add_argument('--delete', metavar='iccid', help='delete profile (warning: this cannot be undone)') + parser.add_argument('--download', nargs=2, metavar=('qr', 'name'), help='download a profile using QR code (format: LPA:1$rsp.truphone.com$QRF-SPEEDTEST)') + parser.add_argument('--nickname', nargs=2, metavar=('iccid', 'name'), help='update the nickname for a profile') + args = parser.parse_args() + + lpa = HARDWARE.get_sim_lpa() + if args.switch: + lpa.switch_profile(args.switch) + elif args.delete: + confirm = input('are you sure you want to delete this profile? (y/N) ') + if confirm == 'y': + lpa.delete_profile(args.delete) + print('deleted profile, please restart device to apply changes') + else: + print('cancelled') + exit(0) + elif args.download: + lpa.download_profile(args.download[0], args.download[1]) + elif args.nickname: + lpa.nickname_profile(args.nickname[0], args.nickname[1]) + else: + parser.print_help() + + profiles = lpa.list_profiles() + print(f'\n{len(profiles)} profile{"s" if len(profiles) > 1 else ""}:') + for p in profiles: + print(f'- {p.iccid} (nickname: {p.nickname or ""}) (provider: {p.provider}) - {"enabled" if p.enabled else "disabled"}') diff --git a/system/hardware/pc/hardware.py b/system/hardware/pc/hardware.py index 017a449c90..9a80f10bed 100644 --- a/system/hardware/pc/hardware.py +++ b/system/hardware/pc/hardware.py @@ -1,12 +1,11 @@ import random from cereal import log -from openpilot.system.hardware.base import HardwareBase +from openpilot.system.hardware.base import HardwareBase, LPABase NetworkType = log.DeviceState.NetworkType NetworkStrength = log.DeviceState.NetworkStrength - class Pc(HardwareBase): def get_os_version(self): return None @@ -41,6 +40,9 @@ class Pc(HardwareBase): 'data_connected': False } + def get_sim_lpa(self) -> LPABase: + raise NotImplementedError("SIM LPA not implemented for PC") + def get_network_strength(self, network_type): return NetworkStrength.unknown diff --git a/system/hardware/tici/esim.py b/system/hardware/tici/esim.py old mode 100755 new mode 100644 index 0a07e77e16..f657966ddf --- a/system/hardware/tici/esim.py +++ b/system/hardware/tici/esim.py @@ -1,28 +1,12 @@ -#!/usr/bin/env python3 - -import argparse import json import os import shutil import subprocess -from dataclasses import dataclass from typing import Literal -@dataclass -class Profile: - iccid: str - nickname: str - enabled: bool - provider: str +from openpilot.system.hardware.base import LPABase, LPAError, LPAProfileNotFoundError, Profile -class LPAError(RuntimeError): - pass - -class LPAProfileNotFoundError(LPAError): - pass - - -class LPA: +class TiciLPA(LPABase): def __init__(self, interface: Literal['qmi', 'at'] = 'qmi'): self.env = os.environ.copy() self.env['LPAC_APDU'] = interface @@ -47,31 +31,13 @@ class LPA: def get_active_profile(self) -> Profile | None: return next((p for p in self.list_profiles() if p.enabled), None) - def enable_profile(self, iccid: str) -> None: - self._validate_profile_exists(iccid) - latest = self.get_active_profile() - if latest: - if latest.iccid == iccid: - return - self.disable_profile(latest.iccid) - self._validate_successful(self._invoke('profile', 'enable', iccid)) - self.process_notifications() - - def disable_profile(self, iccid: str) -> None: - self._validate_profile_exists(iccid) - latest = self.get_active_profile() - if latest is not None and latest.iccid != iccid: - return - self._validate_successful(self._invoke('profile', 'disable', iccid)) - self.process_notifications() - def delete_profile(self, iccid: str) -> None: self._validate_profile_exists(iccid) latest = self.get_active_profile() if latest is not None and latest.iccid == iccid: - self.disable_profile(iccid) + raise LPAError('cannot delete active profile, switch to another profile first') self._validate_successful(self._invoke('profile', 'delete', iccid)) - self.process_notifications() + self._process_notifications() def download_profile(self, qr: str, nickname: str | None = None) -> None: msgs = self._invoke('profile', 'download', '-a', qr) @@ -81,17 +47,24 @@ class LPA: raise LPAError('no new profile found') if nickname: self.nickname_profile(new_profile['payload']['data']['iccid'], nickname) - self.process_notifications() + self._process_notifications() def nickname_profile(self, iccid: str, nickname: str) -> None: self._validate_profile_exists(iccid) self._validate_successful(self._invoke('profile', 'nickname', iccid, nickname)) - def process_notifications(self) -> None: - """ - Process notifications stored on the eUICC, typically to activate/deactivate the profile with the carrier. - """ - self._validate_successful(self._invoke('notification', 'process', '-a', '-r')) + def switch_profile(self, iccid: str) -> None: + self._enable_profile(iccid) + + def _enable_profile(self, iccid: str) -> None: + self._validate_profile_exists(iccid) + latest = self.get_active_profile() + if latest: + if latest.iccid == iccid: + return + self._validate_successful(self._invoke('profile', 'disable', latest.iccid)) + self._validate_successful(self._invoke('profile', 'enable', iccid)) + self._process_notifications() def _invoke(self, *cmd: str): proc = subprocess.Popen(['sudo', '-E', 'lpac'] + list(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env) @@ -124,47 +97,15 @@ class LPA: return messages + def _process_notifications(self) -> None: + """ + Process notifications stored on the eUICC, typically to activate/deactivate the profile with the carrier. + """ + self._validate_successful(self._invoke('notification', 'process', '-a', '-r')) + def _validate_profile_exists(self, iccid: str) -> None: if not any(p.iccid == iccid for p in self.list_profiles()): raise LPAProfileNotFoundError(f'profile {iccid} does not exist') def _validate_successful(self, msgs: list[dict]) -> None: assert msgs[-1]['payload']['message'] == 'success', 'expected success notification' - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(prog='esim.py', description='manage eSIM profiles on your comma device', epilog='comma.ai') - parser.add_argument('--backend', choices=['qmi', 'at'], default='qmi', help='use the specified backend, defaults to qmi') - parser.add_argument('--enable', metavar='iccid', help='enable profile; will disable current profile') - parser.add_argument('--disable', metavar='iccid', help='disable profile') - parser.add_argument('--delete', metavar='iccid', help='delete profile (warning: this cannot be undone)') - parser.add_argument('--download', nargs=2, metavar=('qr', 'name'), help='download a profile using QR code (format: LPA:1$rsp.truphone.com$QRF-SPEEDTEST)') - parser.add_argument('--nickname', nargs=2, metavar=('iccid', 'name'), help='update the nickname for a profile') - args = parser.parse_args() - - lpa = LPA(interface=args.backend) - if args.enable: - lpa.enable_profile(args.enable) - print('enabled profile, please restart device to apply changes') - elif args.disable: - lpa.disable_profile(args.disable) - print('disabled profile, please restart device to apply changes') - elif args.delete: - confirm = input('are you sure you want to delete this profile? (y/N) ') - if confirm == 'y': - lpa.delete_profile(args.delete) - print('deleted profile, please restart device to apply changes') - else: - print('cancelled') - exit(0) - elif args.download: - lpa.download_profile(args.download[0], args.download[1]) - elif args.nickname: - lpa.nickname_profile(args.nickname[0], args.nickname[1]) - else: - parser.print_help() - - profiles = lpa.list_profiles() - print(f'\n{len(profiles)} profile{"s" if len(profiles) > 1 else ""}:') - for p in profiles: - print(f'- {p.iccid} (nickname: {p.nickname or ""}) (provider: {p.provider}) - {"enabled" if p.enabled else "disabled"}') diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 5a8e41b51f..3add52d21c 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -10,8 +10,9 @@ from pathlib import Path from cereal import log from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action -from openpilot.system.hardware.base import HardwareBase, ThermalConfig, ThermalZone +from openpilot.system.hardware.base import HardwareBase, LPABase, ThermalConfig, ThermalZone from openpilot.system.hardware.tici import iwlist +from openpilot.system.hardware.tici.esim import TiciLPA from openpilot.system.hardware.tici.pins import GPIO from openpilot.system.hardware.tici.amplifier import Amplifier @@ -198,6 +199,9 @@ class Tici(HardwareBase): 'data_connected': modem.Get(MM_MODEM, 'State', dbus_interface=DBUS_PROPS, timeout=TIMEOUT) == MM_MODEM_STATE.CONNECTED, } + def get_sim_lpa(self) -> LPABase: + return TiciLPA() + def get_imei(self, slot): if slot != 0: return "" diff --git a/system/hardware/tici/tests/test_esim.py b/system/hardware/tici/tests/test_esim.py index d36bdaa27b..6fab931cce 100644 --- a/system/hardware/tici/tests/test_esim.py +++ b/system/hardware/tici/tests/test_esim.py @@ -1,7 +1,7 @@ import pytest -from openpilot.system.hardware import TICI -from openpilot.system.hardware.tici.esim import LPA, LPAProfileNotFoundError +from openpilot.system.hardware import HARDWARE, TICI +from openpilot.system.hardware.base import LPAProfileNotFoundError # https://euicc-manual.osmocom.org/docs/rsp/known-test-profile # iccid is always the same for the given activation code @@ -11,7 +11,7 @@ TEST_ICCID = '8944476500001944011' TEST_NICKNAME = 'test_profile' def cleanup(): - lpa = LPA() + lpa = HARDWARE.get_sim_lpa() try: lpa.delete_profile(TEST_ICCID) except LPAProfileNotFoundError: @@ -31,7 +31,7 @@ class TestEsim: cleanup() def test_provision_enable_disable(self): - lpa = LPA() + lpa = HARDWARE.get_sim_lpa() current_active = lpa.get_active_profile() lpa.download_profile(TEST_ACTIVATION_CODE, TEST_NICKNAME) diff --git a/tools/op.sh b/tools/op.sh index 7c17c949e4..9142d50a18 100755 --- a/tools/op.sh +++ b/tools/op.sh @@ -295,7 +295,7 @@ function op_check() { function op_esim() { op_before_cmd - op_run_command system/hardware/tici/esim.py "$@" + op_run_command system/hardware/esim.py "$@" } function op_build() { From eabee8c73f3c8fc4dcadb47f04ee527b99c44a03 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Sun, 25 May 2025 09:33:37 -0700 Subject: [PATCH 332/477] bump msgq (#35345) --- msgq_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msgq_repo b/msgq_repo index 94e738992c..5483a02de3 160000 --- a/msgq_repo +++ b/msgq_repo @@ -1 +1 @@ -Subproject commit 94e738992ca8a12c2d9bb7bdeeaab4713543773e +Subproject commit 5483a02de303d40cb2632d59f3f3a54dabfb5965 From 22715464b97365b60ca812b23bc3c6f6e9001d3e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 26 May 2025 00:47:41 +0800 Subject: [PATCH 333/477] system/ui: throttle camera connection attempts (#35343) throttle connection attempts --- system/ui/widgets/cameraview.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index 01aac0370b..94625ab19d 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -5,6 +5,8 @@ from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage +CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts + VERTEX_SHADER = """ #version 300 es in vec3 vertexPosition; @@ -53,6 +55,7 @@ else: class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): self.client = VisionIpcClient(name, stream_type, False) + self.last_connection_attempt: float = 0.0 self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER) self.frame: VisionBuf | None = None @@ -159,6 +162,14 @@ class CameraView: def _ensure_connection(self) -> bool: if not self.client.is_connected(): self.frame = None + + # Throttle connection attempts + current_time = rl.get_time() + if current_time - self.last_connection_attempt < CONNECTION_RETRY_INTERVAL: + return False + + self.last_connection_attempt = current_time + if not self.client.connect(False) or not self.client.num_buffers: return False From 193df11a1c22b85fe914b772de66c0ce49a36354 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 27 May 2025 00:29:29 +0800 Subject: [PATCH 334/477] system/ui: avoid redundant texture updates (#35347) avoid redundant texture updates --- system/ui/widgets/cameraview.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index 94625ab19d..fe53507cef 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -55,6 +55,7 @@ else: class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): self.client = VisionIpcClient(name, stream_type, False) + self._texture_needs_update = True self.last_connection_attempt: float = 0.0 self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER) @@ -95,6 +96,7 @@ class CameraView: # Try to get a new buffer without blocking buffer = self.client.recv(timeout_ms=0) if buffer: + self._texture_needs_update = True self.frame = buffer if not self.frame: @@ -147,11 +149,13 @@ class CameraView: return # Update textures with new frame data - y_data = self.frame.data[: self.frame.uv_offset] - uv_data = self.frame.data[self.frame.uv_offset :] + if self._texture_needs_update: + y_data = self.frame.data[: self.frame.uv_offset] + uv_data = self.frame.data[self.frame.uv_offset :] - rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data)) - rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data)) + rl.update_texture(self.texture_y, rl.ffi.cast("void *", y_data.ctypes.data)) + rl.update_texture(self.texture_uv, rl.ffi.cast("void *", uv_data.ctypes.data)) + self._texture_needs_update = False # Render with shader rl.begin_shader_mode(self.shader) From 3d3e9599d804149e0f3036cb46ec1b74fbc0c04c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 27 May 2025 01:42:50 +0800 Subject: [PATCH 335/477] system/ui: add specialized camera views with custom transformations (#35346) * add specialized camera views with custom transformations for driver and road * improve * return np array * cached matrix --- system/ui/onroad/augmented_road_view.py | 127 ++++++++++++++++++++++++ system/ui/widgets/cameraview.py | 39 +++++++- system/ui/widgets/driver_camera_view.py | 46 +++++++++ 3 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 system/ui/onroad/augmented_road_view.py create mode 100644 system/ui/widgets/driver_camera_view.py diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py new file mode 100644 index 0000000000..58a37460d5 --- /dev/null +++ b/system/ui/onroad/augmented_road_view.py @@ -0,0 +1,127 @@ +import numpy as np +import pyray as rl + +from cereal import messaging, log +from msgq.visionipc import VisionStreamType +from openpilot.system.ui.widgets.cameraview import CameraView +from openpilot.system.ui.lib.application import gui_app +from openpilot.common.transformations.camera import DEVICE_CAMERAS, DeviceCameraConfig, view_frame_from_device_frame +from openpilot.common.transformations.orientation import rot_from_euler + + +CALIBRATED = log.LiveCalibrationData.Status.calibrated +DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"] + + +class AugmentedRoadView(CameraView): + def __init__(self, sm: messaging.SubMaster, stream_type: VisionStreamType): + super().__init__("camerad", stream_type) + + self.sm = sm + self.stream_type = stream_type + self.is_wide_camera = stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD + + self.device_camera: DeviceCameraConfig | None = None + self.view_from_calib = view_frame_from_device_frame.copy() + self.view_from_wide_calib = view_frame_from_device_frame.copy() + + self._last_calib_time: float = 0 + self._last_recect_dims = (0.0, 0.0) + self._cacheed_matrix: np.ndarray | None = None + + def render(self, rect): + # Update calibration before rendering + self._update_calibration() + + # Render the base camera view + super().render(rect) + + # TODO: Add road visualization overlays like: + # - Lane lines and road edges + # - Path prediction + # - Lead vehicle indicators + # - Additional features + + def _update_calibration(self): + # Update device camera if not already set + if not self.device_camera and sm.seen['roadCameraState'] and sm.seen['deviceState']: + self.device_camera = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] + + # Check if live calibration data is available and valid + if not (sm.updated["liveCalibration"] and sm.valid['liveCalibration']): + return + + calib = self.sm['liveCalibration'] + if len(calib.rpyCalib) != 3 or calib.calStatus != CALIBRATED: + return + + # Update view_from_calib matrix + device_from_calib = rot_from_euler(calib.rpyCalib) + self.view_from_calib = view_frame_from_device_frame @ device_from_calib + + # Update wide calibration if available + if hasattr(calib, 'wideFromDeviceEuler') and len(calib.wideFromDeviceEuler) == 3: + wide_from_device = rot_from_euler(calib.wideFromDeviceEuler) + self.view_from_wide_calib = view_frame_from_device_frame @ wide_from_device @ device_from_calib + + def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: + # Check if we can use cached matrix + calib_time = self.sm.recv_frame.get('liveCalibration', 0) + current_dims = (rect.width, rect.height) + if (self._last_calib_time == calib_time and + self._last_recect_dims == current_dims and + self._cacheed_matrix is not None): + return self._cacheed_matrix + + # Get camera configuration + device_camera = self.device_camera or DEFAULT_DEVICE_CAMERA + intrinsic = device_camera.ecam.intrinsics if self.is_wide_camera else device_camera.fcam.intrinsics + calibration = self.view_from_wide_calib if self.is_wide_camera else self.view_from_calib + zoom = 2.0 if self.is_wide_camera else 1.1 + + # Calculate transforms for vanishing point + inf_point = np.array([1000.0, 0.0, 0.0]) + calib_transform = intrinsic @ calibration + kep = calib_transform @ inf_point + + # Calculate center points and dimensions + w, h = current_dims + cx, cy = intrinsic[0, 2], intrinsic[1, 2] + + # Calculate max allowed offsets with margins + margin = 5 + max_x_offset = cx * zoom - w / 2 - margin + max_y_offset = cy * zoom - h / 2 - margin + + # Calculate and clamp offsets to prevent out-of-bounds issues + try: + if abs(kep[2]) > 1e-6: + x_offset = np.clip((kep[0] / kep[2] - cx) * zoom, -max_x_offset, max_x_offset) + y_offset = np.clip((kep[1] / kep[2] - cy) * zoom, -max_y_offset, max_y_offset) + else: + x_offset, y_offset = 0, 0 + except (ZeroDivisionError, OverflowError): + x_offset, y_offset = 0, 0 + + # Update cache values + self._last_calib_time = calib_time + self._last_recect_dims = current_dims + self._cacheed_matrix = np.array([ + [zoom * 2 * cx / w, 0, -x_offset / w * 2], + [0, zoom * 2 * cy / h, -y_offset / h * 2], + [0, 0, 1.0] + ]) + + return self._cacheed_matrix + + +if __name__ == "__main__": + gui_app.init_window("OnRoad Camera View") + sm = messaging.SubMaster(['deviceState', 'liveCalibration', 'roadCameraState']) + road_camera_view = AugmentedRoadView(sm, VisionStreamType.VISION_STREAM_ROAD) + try: + for _ in gui_app.render(): + sm.update(0) + road_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + finally: + road_camera_view.close() diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index fe53507cef..bff13a7910 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -1,4 +1,6 @@ +import numpy as np import pyray as rl + from openpilot.system.hardware import TICI from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf from openpilot.system.ui.lib.application import gui_app @@ -89,6 +91,24 @@ class CameraView: if self.shader and self.shader.id: rl.unload_shader(self.shader) + def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: + if not self.frame: + return np.eye(3) + + # Calculate aspect ratios + widget_aspect_ratio = rect.width / rect.height + frame_aspect_ratio = self.frame.width / self.frame.height + + # Calculate scaling factors to maintain aspect ratio + zx = min(frame_aspect_ratio / widget_aspect_ratio, 1.0) + zy = min(widget_aspect_ratio / frame_aspect_ratio, 1.0) + + return np.array([ + [zx, 0.0, 0.0], + [0.0, zy, 0.0], + [0.0, 0.0, 1.0] + ]) + def render(self, rect: rl.Rectangle): if not self._ensure_connection(): return @@ -102,12 +122,21 @@ class CameraView: if not self.frame: return - # Calculate scaling to maintain aspect ratio - scale = min(rect.width / self.frame.width, rect.height / self.frame.height) - x_offset = rect.x + (rect.width - (self.frame.width * scale)) / 2 - y_offset = rect.y + (rect.height - (self.frame.height * scale)) / 2 + transform = self._calc_frame_matrix(rect) src_rect = rl.Rectangle(0, 0, float(self.frame.width), float(self.frame.height)) - dst_rect = rl.Rectangle(x_offset, y_offset, self.frame.width * scale, self.frame.height * scale) + + # Calculate scale + scale_x = rect.width * transform[0, 0] # zx + scale_y = rect.height * transform[1, 1] # zy + + # Calculate base position (centered) + x_offset = rect.x + (rect.width - scale_x) / 2 + y_offset = rect.y + (rect.height - scale_y) / 2 + + x_offset += transform[0, 2] * rect.width / 2 + y_offset += transform[1, 2] * rect.height / 2 + + dst_rect = rl.Rectangle(x_offset, y_offset, scale_x, scale_y) # Render with appropriate method if TICI: diff --git a/system/ui/widgets/driver_camera_view.py b/system/ui/widgets/driver_camera_view.py new file mode 100644 index 0000000000..174fac378d --- /dev/null +++ b/system/ui/widgets/driver_camera_view.py @@ -0,0 +1,46 @@ +import numpy as np +import pyray as rl +from openpilot.system.ui.widgets.cameraview import CameraView +from msgq.visionipc import VisionStreamType +from openpilot.system.ui.lib.application import gui_app + + +class DriverCameraView(CameraView): + def __init__(self, stream_type: VisionStreamType): + super().__init__("camerad", stream_type) + + def render(self, rect): + super().render(rect) + + # TODO: Add additional rendering logic + + def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: + driver_view_ratio = 2.0 + + # Get stream dimensions + if self.frame: + stream_width = self.frame.width + stream_height = self.frame.height + else: + # Default values if frame not available + stream_width = 1928 + stream_height = 1208 + + yscale = stream_height * driver_view_ratio / stream_width + xscale = yscale * rect.height / rect.width * stream_width / stream_height + + return np.array([ + [xscale, 0.0, 0.0], + [0.0, yscale, 0.0], + [0.0, 0.0, 1.0] + ]) + + +if __name__ == "__main__": + gui_app.init_window("Driver Camera View") + driver_camera_view = DriverCameraView(VisionStreamType.VISION_STREAM_DRIVER) + try: + for _ in gui_app.render(): + driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + finally: + driver_camera_view.close() From 8ffcddce30b07dd7871189d01ec9bbc1a6fbedeb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 11:02:14 -0700 Subject: [PATCH 336/477] [bot] Update translations (#35348) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_de.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_es.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_fr.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_ja.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_ko.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_pt-BR.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_th.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_tr.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_zh-CHS.ts | 28 ++++++++++++++++++++---- selfdrive/ui/translations/main_zh-CHT.ts | 28 ++++++++++++++++++++---- 11 files changed, 264 insertions(+), 44 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index fa3b2d69e7..603d6a01cd 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -62,10 +62,6 @@ Cellular Metered محدود بالاتصال الخلوي - - Prevent large data uploads when on a metered connection - منع تحميل البيانات الكبيرة عندما يكون الاتصال محدوداً - Hidden Network شبكة مخفية @@ -86,6 +82,30 @@ for "%1" من أجل "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 3123e52578..aa6cf75c30 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -62,10 +62,6 @@ Cellular Metered Getaktete Verbindung - - Prevent large data uploads when on a metered connection - Hochladen großer Dateien über getaktete Verbindungen unterbinden - Hidden Network Verborgenes Netzwerk @@ -86,6 +82,30 @@ for "%1" für "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 3d9be63caf..64272244ea 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -62,10 +62,6 @@ Cellular Metered Plano de datos limitado - - Prevent large data uploads when on a metered connection - Evitar grandes descargas de datos cuando tenga una conexión limitada - Hidden Network Red Oculta @@ -86,6 +82,30 @@ for "%1" para "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index 69652510df..e7c4fa506a 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -62,10 +62,6 @@ Cellular Metered Connexion cellulaire limitée - - Prevent large data uploads when on a metered connection - Éviter les transferts de données importants sur une connexion limitée - Hidden Network Réseau Caché @@ -86,6 +82,30 @@ for "%1" pour "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 4cce79757f..8fff080283 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -62,10 +62,6 @@ Cellular Metered 従量制通信設定 - - Prevent large data uploads when on a metered connection - 大量のデータのアップロードを防止する - Hidden Network ネットワーク非表示 @@ -86,6 +82,30 @@ for "%1" [%1] + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 3be96846a7..268475f917 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -62,10 +62,6 @@ Cellular Metered 데이터 요금제 - - Prevent large data uploads when on a metered connection - 데이터 요금제 연결 시 대용량 데이터 업로드를 방지합니다 - Hidden Network 숨겨진 네트워크 @@ -86,6 +82,30 @@ for "%1" "%1"에 접속하려면 비밀번호가 필요합니다 + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index a6a473bc92..c5e4926724 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -62,10 +62,6 @@ Cellular Metered Plano de Dados Limitado - - Prevent large data uploads when on a metered connection - Evite grandes uploads de dados quando estiver em uma conexão limitada - Hidden Network Rede Oculta @@ -86,6 +82,30 @@ for "%1" para "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index c7521082c3..e04cdac3e3 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -62,10 +62,6 @@ Cellular Metered ลดการส่งข้อมูลผ่านเซลลูล่าร์ - - Prevent large data uploads when on a metered connection - ปิดการอัพโหลดข้อมูลขนาดใหญ่เมื่อเชื่อมต่อผ่านเซลลูล่าร์ - Hidden Network เครือข่ายที่ซ่อนอยู่ @@ -86,6 +82,30 @@ for "%1" สำหรับ "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index b8b7202898..181c9c44f3 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -62,10 +62,6 @@ Cellular Metered - - Prevent large data uploads when on a metered connection - - Hidden Network @@ -86,6 +82,30 @@ for "%1" için "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index c310265a1c..03e7a960be 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -62,10 +62,6 @@ Cellular Metered 按流量计费的手机移动网络 - - Prevent large data uploads when on a metered connection - 当使用按流量计费的连接时,避免上传大流量数据 - Hidden Network 隐藏的网络 @@ -86,6 +82,30 @@ for "%1" 网络名称:"%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 5f81cc2c53..e28ac44b98 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -62,10 +62,6 @@ Cellular Metered 行動網路 - - Prevent large data uploads when on a metered connection - 防止使用行動網路上傳大量的數據 - Hidden Network 隱藏的網路 @@ -86,6 +82,30 @@ for "%1" 給 "%1" + + Prevent large data uploads when on a metered cellular connection + + + + default + + + + metered + + + + unmetered + + + + Wi-Fi Network Metered + + + + Prevent large data uploads when on a metered Wi-FI connection + + ConfirmationDialog From 927ce0bc065a3419cc6be6c50ce24e4fd8bbb154 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 27 May 2025 02:10:32 +0800 Subject: [PATCH 337/477] system/ui: fix typos (#35349) fix typos --- system/ui/onroad/augmented_road_view.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 58a37460d5..7ae8f37c13 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -26,8 +26,8 @@ class AugmentedRoadView(CameraView): self.view_from_wide_calib = view_frame_from_device_frame.copy() self._last_calib_time: float = 0 - self._last_recect_dims = (0.0, 0.0) - self._cacheed_matrix: np.ndarray | None = None + self._last_rect_dims = (0.0, 0.0) + self._cached_matrix: np.ndarray | None = None def render(self, rect): # Update calibration before rendering @@ -69,9 +69,9 @@ class AugmentedRoadView(CameraView): calib_time = self.sm.recv_frame.get('liveCalibration', 0) current_dims = (rect.width, rect.height) if (self._last_calib_time == calib_time and - self._last_recect_dims == current_dims and - self._cacheed_matrix is not None): - return self._cacheed_matrix + self._last_rect_dims == current_dims and + self._cached_matrix is not None): + return self._cached_matrix # Get camera configuration device_camera = self.device_camera or DEFAULT_DEVICE_CAMERA @@ -105,14 +105,14 @@ class AugmentedRoadView(CameraView): # Update cache values self._last_calib_time = calib_time - self._last_recect_dims = current_dims - self._cacheed_matrix = np.array([ + self._last_rect_dims = current_dims + self._cached_matrix = np.array([ [zoom * 2 * cx / w, 0, -x_offset / w * 2], [0, zoom * 2 * cy / h, -y_offset / h * 2], [0, 0, 1.0] ]) - return self._cacheed_matrix + return self._cached_matrix if __name__ == "__main__": From 427a61ab121ff270b58095155d084b6aa3b407e3 Mon Sep 17 00:00:00 2001 From: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Date: Mon, 26 May 2025 17:07:56 -0300 Subject: [PATCH 338/477] fix Wi-FI typo on networking.cc (#35350) --- selfdrive/ui/qt/network/networking.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index bb914b6449..f19786a46f 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -184,7 +184,7 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid // Wi-Fi metered toggle std::vector metered_button_texts{tr("default"), tr("metered"), tr("unmetered")}; - wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-FI connection"), "", metered_button_texts); + wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-Fi connection"), "", metered_button_texts); QObject::connect(wifiMeteredToggle, &MultiButtonControl::buttonClicked, [=](int id) { wifiMeteredToggle->setEnabled(false); MeteredType metered = MeteredType::UNKNOWN; From 44d233337d9060b236a69b0536d725e4526e975d Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 26 May 2025 13:08:28 -0700 Subject: [PATCH 339/477] esim: faster switching (#35344) * esim: lpac doesnt need disable here * more red diff --- system/hardware/tici/esim.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/system/hardware/tici/esim.py b/system/hardware/tici/esim.py index f657966ddf..b489286f50 100644 --- a/system/hardware/tici/esim.py +++ b/system/hardware/tici/esim.py @@ -54,15 +54,10 @@ class TiciLPA(LPABase): self._validate_successful(self._invoke('profile', 'nickname', iccid, nickname)) def switch_profile(self, iccid: str) -> None: - self._enable_profile(iccid) - - def _enable_profile(self, iccid: str) -> None: self._validate_profile_exists(iccid) latest = self.get_active_profile() - if latest: - if latest.iccid == iccid: - return - self._validate_successful(self._invoke('profile', 'disable', latest.iccid)) + if latest and latest.iccid == iccid: + return self._validate_successful(self._invoke('profile', 'enable', iccid)) self._process_notifications() From 1fb5744685b323b71d8ae43be8bf37172d1e9c51 Mon Sep 17 00:00:00 2001 From: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Date: Mon, 26 May 2025 17:08:54 -0300 Subject: [PATCH 340/477] Multilang: update pt-BR translations (#35351) --- selfdrive/ui/translations/main_pt-BR.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index c5e4926724..2a7d270713 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -88,23 +88,23 @@ default - + padrão metered - + limitada unmetered - + ilimitada Wi-Fi Network Metered - + Rede Wi-Fi com Franquia Prevent large data uploads when on a metered Wi-FI connection - + Previna o envio de grandes volumes de dados em conexões Wi-Fi com franquia de limite de dados From 742e30495a7368ac1143b3ae8ed5f909d07b4de3 Mon Sep 17 00:00:00 2001 From: Cameron Clough Date: Tue, 27 May 2025 01:25:56 +0100 Subject: [PATCH 341/477] Reapply "ui: rewrite installer using raylib, remove qt (#33756)" (#35308) This reverts commit 472feefcfdf20a8f4823e28baf596868fd70391b. --- selfdrive/ui/SConscript | 26 ++-- selfdrive/ui/installer/installer.cc | 182 +++++++++++-------------- selfdrive/ui/installer/installer.h | 28 ---- selfdrive/ui/installer/inter-ascii.ttf | 3 + 4 files changed, 97 insertions(+), 142 deletions(-) delete mode 100644 selfdrive/ui/installer/installer.h create mode 100644 selfdrive/ui/installer/inter-ascii.ttf diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e63359da05..7199399d41 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,5 @@ -import os import json -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -76,8 +75,15 @@ if GetOption('extras'): if arch != "Darwin": # build installers - senv = qt_env.Clone() - senv['LINKFLAGS'].append('-Wl,-strip-debug') + raylib_env = env.Clone() + raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] + raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') + + raylib_libs = common + ["raylib"] + if arch == "larch64": + raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"] + else: + raylib_libs += ["GL"] release = "release3" installers = [ @@ -87,17 +93,19 @@ if GetOption('extras'): ("openpilot_internal", "nightly-dev"), ] - cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh", - "ld -r -b binary -o $TARGET $SOURCE") + cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", + "ld -r -b binary -o $TARGET $SOURCE") + inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", + "ld -r -b binary -o $TARGET $SOURCE") for name, branch in installers: d = {'BRANCH': f"'\"{branch}\"'"} if "internal" in name: d['INTERNAL'] = "1" - obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) - f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs) + obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) + f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) # keep installers small - assert f[0].get_size() < 370*1e3 + assert f[0].get_size() < 1300*1e3, f[0].get_size() # build watch3 if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'): diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 84486e61be..7326e089ab 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -1,19 +1,15 @@ -#include - -#include +#include +#include #include #include -#include - -#include -#include -#include -#include +#include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/ui/installer/installer.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" +#include "third_party/raylib/include/raylib.h" + +int freshClone(); +int cachedFetch(const std::string &cache); +int executeGitCommand(const std::string &cmd); std::string get_str(std::string const s) { std::string::size_type pos = s.find('?'); @@ -28,136 +24,108 @@ const std::string BRANCH_STR = get_str(BRANCH "? #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" -const QString CACHE_PATH = "/data/openpilot.cache"; +const std::string CACHE_PATH = "/data/openpilot.cache"; #define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); +extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); +extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end"); + +Font font; void run(const char* cmd) { int err = std::system(cmd); assert(err == 0); } -Installer::Installer(QWidget *parent) : QWidget(parent) { - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(150, 290, 150, 150); - layout->setSpacing(0); - - QLabel *title = new QLabel(tr("Installing...")); - title->setStyleSheet("font-size: 90px; font-weight: 600;"); - layout->addWidget(title, 0, Qt::AlignTop); - - layout->addSpacing(170); - - bar = new QProgressBar(); - bar->setRange(0, 100); - bar->setTextVisible(false); - bar->setFixedHeight(72); - layout->addWidget(bar, 0, Qt::AlignTop); - - layout->addSpacing(30); - - val = new QLabel("0%"); - val->setStyleSheet("font-size: 70px; font-weight: 300;"); - layout->addWidget(val, 0, Qt::AlignTop); - - layout->addStretch(); - - QObject::connect(&proc, QOverload::of(&QProcess::finished), this, &Installer::cloneFinished); - QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress); - - QTimer::singleShot(100, this, &Installer::doInstall); - - setStyleSheet(R"( - * { - font-family: Inter; - color: white; - background-color: black; - } - QProgressBar { - border: none; - background-color: #292929; - } - QProgressBar::chunk { - background-color: #364DEF; - } - )"); +void renderProgress(int progress) { + BeginDrawing(); + ClearBackground(BLACK); + DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE); + Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72}; + DrawRectangleRec(bar, (Color){41, 41, 41, 255}); + progress = std::clamp(progress, 0, 100); + bar.width *= progress / 100.0f; + DrawRectangleRec(bar, (Color){70, 91, 234, 255}); + DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE); + EndDrawing(); } -void Installer::updateProgress(int percent) { - bar->setValue(percent); - val->setText(QString("%1%").arg(percent)); - update(); -} - -void Installer::doInstall() { +int doInstall() { // wait for valid time while (!util::system_time_valid()) { - usleep(500 * 1000); - qDebug() << "Waiting for valid time"; + util::sleep_for(500); + LOGD("Waiting for valid time"); } // cleanup previous install attempts run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); // do the install - if (QDir(CACHE_PATH).exists()) { - cachedFetch(CACHE_PATH); + if (util::file_exists(CACHE_PATH)) { + return cachedFetch(CACHE_PATH); } else { - freshClone(); + return freshClone(); } } -void Installer::freshClone() { - qDebug() << "Doing fresh clone"; - proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(), - "--depth=1", "--recurse-submodules", TMP_INSTALL_PATH}); +int freshClone() { + LOGD("Doing fresh clone"); + std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", + GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); + return executeGitCommand(cmd); } -void Installer::cachedFetch(const QString &cache) { - qDebug() << "Fetching with cache: " << cache; +int cachedFetch(const std::string &cache) { + LOGD("Fetching with cache: %s", cache.c_str()); - run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str()); - int err = chdir(TMP_INSTALL_PATH); - assert(err == 0); - run(("git remote set-branches --add origin " + BRANCH_STR).c_str()); + run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); + run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); - updateProgress(10); + renderProgress(10); - proc.setWorkingDirectory(TMP_INSTALL_PATH); - proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()}); + return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); } -void Installer::readProgress() { - const QVector> stages = { +int executeGitCommand(const std::string &cmd) { + static const std::array stages = { // prefix, weight in percentage - {"Receiving objects: ", 91}, - {"Resolving deltas: ", 2}, - {"Updating files: ", 7}, + std::pair{"Receiving objects: ", 91}, + std::pair{"Resolving deltas: ", 2}, + std::pair{"Updating files: ", 7}, }; - auto line = QString(proc.readAllStandardError()); + FILE *pipe = popen(cmd.c_str(), "r"); + if (!pipe) return -1; - int base = 0; - for (const QPair kv : stages) { - if (line.startsWith(kv.first)) { - auto perc = line.split(kv.first)[1].split("%")[0]; - int p = base + int(perc.toFloat() / 100. * kv.second); - updateProgress(p); - break; + char buffer[512]; + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + std::string line(buffer); + int base = 0; + for (const auto &[text, weight] : stages) { + if (line.find(text) != std::string::npos) { + size_t percentPos = line.find("%"); + if (percentPos != std::string::npos && percentPos >= 3) { + int percent = std::stoi(line.substr(percentPos - 3, 3)); + int progress = base + int(percent / 100. * weight); + renderProgress(progress); + } + break; + } + base += weight; } - base += kv.second; } + return pclose(pipe); } -void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "git finished with " << exitCode; +void cloneFinished(int exitCode) { + LOGD("git finished with %d", exitCode); assert(exitCode == 0); - updateProgress(100); + renderProgress(100); // ensure correct branch is checked out int err = chdir(TMP_INSTALL_PATH); @@ -203,13 +171,17 @@ void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over - QTimer::singleShot(60 * 1000, &QCoreApplication::quit); + util::sleep_for(60 * 1000); } int main(int argc, char *argv[]) { - initApp(argc, argv); - QApplication a(argc, argv); - Installer installer; - setMainWindow(&installer); - return a.exec(); + InitWindow(2160, 1080, "Installer"); + font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); + SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + renderProgress(0); + int result = doInstall(); + cloneFinished(result); + CloseWindow(); + UnloadFont(font); + return 0; } diff --git a/selfdrive/ui/installer/installer.h b/selfdrive/ui/installer/installer.h deleted file mode 100644 index de3af0ff39..0000000000 --- a/selfdrive/ui/installer/installer.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class Installer : public QWidget { - Q_OBJECT - -public: - explicit Installer(QWidget *parent = 0); - -private slots: - void updateProgress(int percent); - - void readProgress(); - void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus); - -private: - QLabel *val; - QProgressBar *bar; - QProcess proc; - - void doInstall(); - void freshClone(); - void cachedFetch(const QString &cache); -}; diff --git a/selfdrive/ui/installer/inter-ascii.ttf b/selfdrive/ui/installer/inter-ascii.ttf new file mode 100644 index 0000000000..5d480c515a --- /dev/null +++ b/selfdrive/ui/installer/inter-ascii.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ef26a4099ef867f3493389379d882381a2491cdbfa41a086be8899a2154dcb3 +size 26160 From 50aaa691373222dcc71c506c1d055e21c5bc97ea Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 26 May 2025 17:49:44 -0700 Subject: [PATCH 342/477] sensord: cleanup, prep for rewrite (#35352) * rm bmx * thanks claude * fix * fix mypy --- system/sensord/SConscript | 4 - system/sensord/sensors/bmx055_accel.cc | 85 -------- system/sensord/sensors/bmx055_accel.h | 41 ---- system/sensord/sensors/bmx055_gyro.cc | 92 --------- system/sensord/sensors/bmx055_gyro.h | 41 ---- system/sensord/sensors/bmx055_magn.cc | 258 ------------------------- system/sensord/sensors/bmx055_magn.h | 64 ------ system/sensord/sensors/bmx055_temp.cc | 31 --- system/sensord/sensors/bmx055_temp.h | 13 -- system/sensord/sensors_qcom2.cc | 9 - system/sensord/tests/test_sensord.py | 30 +-- system/sensord/tests/ttff_test.py | 48 ----- 12 files changed, 5 insertions(+), 711 deletions(-) delete mode 100644 system/sensord/sensors/bmx055_accel.cc delete mode 100644 system/sensord/sensors/bmx055_accel.h delete mode 100644 system/sensord/sensors/bmx055_gyro.cc delete mode 100644 system/sensord/sensors/bmx055_gyro.h delete mode 100644 system/sensord/sensors/bmx055_magn.cc delete mode 100644 system/sensord/sensors/bmx055_magn.h delete mode 100644 system/sensord/sensors/bmx055_temp.cc delete mode 100644 system/sensord/sensors/bmx055_temp.h delete mode 100755 system/sensord/tests/ttff_test.py diff --git a/system/sensord/SConscript b/system/sensord/SConscript index e2dfb522c6..1222f0bfcd 100644 --- a/system/sensord/SConscript +++ b/system/sensord/SConscript @@ -2,10 +2,6 @@ Import('env', 'arch', 'common', 'messaging') sensors = [ 'sensors/i2c_sensor.cc', - 'sensors/bmx055_accel.cc', - 'sensors/bmx055_gyro.cc', - 'sensors/bmx055_magn.cc', - 'sensors/bmx055_temp.cc', 'sensors/lsm6ds3_accel.cc', 'sensors/lsm6ds3_gyro.cc', 'sensors/lsm6ds3_temp.cc', diff --git a/system/sensord/sensors/bmx055_accel.cc b/system/sensord/sensors/bmx055_accel.cc deleted file mode 100644 index bcc31e1d6c..0000000000 --- a/system/sensord/sensors/bmx055_accel.cc +++ /dev/null @@ -1,85 +0,0 @@ -#include "system/sensord/sensors/bmx055_accel.h" - -#include - -#include "common/swaglog.h" -#include "common/timing.h" -#include "common/util.h" - -BMX055_Accel::BMX055_Accel(I2CBus *bus) : I2CSensor(bus) {} - -int BMX055_Accel::init() { - int ret = verify_chip_id(BMX055_ACCEL_I2C_REG_ID, {BMX055_ACCEL_CHIP_ID}); - if (ret == -1) { - goto fail; - } - - ret = set_register(BMX055_ACCEL_I2C_REG_PMU, BMX055_ACCEL_NORMAL_MODE); - if (ret < 0) { - goto fail; - } - - // bmx055 accel has a 1.3ms wakeup time from deep suspend mode - util::sleep_for(10); - - // High bandwidth - // ret = set_register(BMX055_ACCEL_I2C_REG_HBW, BMX055_ACCEL_HBW_ENABLE); - // if (ret < 0) { - // goto fail; - // } - - // Low bandwidth - ret = set_register(BMX055_ACCEL_I2C_REG_HBW, BMX055_ACCEL_HBW_DISABLE); - if (ret < 0) { - goto fail; - } - - ret = set_register(BMX055_ACCEL_I2C_REG_BW, BMX055_ACCEL_BW_125HZ); - if (ret < 0) { - goto fail; - } - - enabled = true; - -fail: - return ret; -} - -int BMX055_Accel::shutdown() { - if (!enabled) return 0; - - // enter deep suspend mode (lowest power mode) - int ret = set_register(BMX055_ACCEL_I2C_REG_PMU, BMX055_ACCEL_DEEP_SUSPEND); - if (ret < 0) { - LOGE("Could not move BMX055 ACCEL in deep suspend mode!"); - } - - return ret; -} - -bool BMX055_Accel::get_event(MessageBuilder &msg, uint64_t ts) { - uint64_t start_time = nanos_since_boot(); - uint8_t buffer[6]; - int len = read_register(BMX055_ACCEL_I2C_REG_X_LSB, buffer, sizeof(buffer)); - assert(len == 6); - - // 12 bit = +-2g - float scale = 9.81 * 2.0f / (1 << 11); - float x = -read_12_bit(buffer[0], buffer[1]) * scale; - float y = -read_12_bit(buffer[2], buffer[3]) * scale; - float z = read_12_bit(buffer[4], buffer[5]) * scale; - - auto event = msg.initEvent().initAccelerometer2(); - event.setSource(cereal::SensorEventData::SensorSource::BMX055); - event.setVersion(1); - event.setSensor(SENSOR_ACCELEROMETER); - event.setType(SENSOR_TYPE_ACCELEROMETER); - event.setTimestamp(start_time); - - float xyz[] = {x, y, z}; - auto svec = event.initAcceleration(); - svec.setV(xyz); - svec.setStatus(true); - - return true; -} diff --git a/system/sensord/sensors/bmx055_accel.h b/system/sensord/sensors/bmx055_accel.h deleted file mode 100644 index 2cc316e992..0000000000 --- a/system/sensord/sensors/bmx055_accel.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define BMX055_ACCEL_I2C_ADDR 0x18 - -// Registers of the chip -#define BMX055_ACCEL_I2C_REG_ID 0x00 -#define BMX055_ACCEL_I2C_REG_X_LSB 0x02 -#define BMX055_ACCEL_I2C_REG_TEMP 0x08 -#define BMX055_ACCEL_I2C_REG_BW 0x10 -#define BMX055_ACCEL_I2C_REG_PMU 0x11 -#define BMX055_ACCEL_I2C_REG_HBW 0x13 -#define BMX055_ACCEL_I2C_REG_FIFO 0x3F - -// Constants -#define BMX055_ACCEL_CHIP_ID 0xFA - -#define BMX055_ACCEL_HBW_ENABLE 0b10000000 -#define BMX055_ACCEL_HBW_DISABLE 0b00000000 -#define BMX055_ACCEL_DEEP_SUSPEND 0b00100000 -#define BMX055_ACCEL_NORMAL_MODE 0b00000000 - -#define BMX055_ACCEL_BW_7_81HZ 0b01000 -#define BMX055_ACCEL_BW_15_63HZ 0b01001 -#define BMX055_ACCEL_BW_31_25HZ 0b01010 -#define BMX055_ACCEL_BW_62_5HZ 0b01011 -#define BMX055_ACCEL_BW_125HZ 0b01100 -#define BMX055_ACCEL_BW_250HZ 0b01101 -#define BMX055_ACCEL_BW_500HZ 0b01110 -#define BMX055_ACCEL_BW_1000HZ 0b01111 - -class BMX055_Accel : public I2CSensor { - uint8_t get_device_address() {return BMX055_ACCEL_I2C_ADDR;} -public: - BMX055_Accel(I2CBus *bus); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown(); -}; diff --git a/system/sensord/sensors/bmx055_gyro.cc b/system/sensord/sensors/bmx055_gyro.cc deleted file mode 100644 index 0cc405f654..0000000000 --- a/system/sensord/sensors/bmx055_gyro.cc +++ /dev/null @@ -1,92 +0,0 @@ -#include "system/sensord/sensors/bmx055_gyro.h" - -#include -#include - -#include "common/swaglog.h" -#include "common/util.h" - -#define DEG2RAD(x) ((x) * M_PI / 180.0) - - -BMX055_Gyro::BMX055_Gyro(I2CBus *bus) : I2CSensor(bus) {} - -int BMX055_Gyro::init() { - int ret = verify_chip_id(BMX055_GYRO_I2C_REG_ID, {BMX055_GYRO_CHIP_ID}); - if (ret == -1) return -1; - - ret = set_register(BMX055_GYRO_I2C_REG_LPM1, BMX055_GYRO_NORMAL_MODE); - if (ret < 0) { - goto fail; - } - // bmx055 gyro has a 30ms wakeup time from deep suspend mode - util::sleep_for(50); - - // High bandwidth - // ret = set_register(BMX055_GYRO_I2C_REG_HBW, BMX055_GYRO_HBW_ENABLE); - // if (ret < 0) { - // goto fail; - // } - - // Low bandwidth - ret = set_register(BMX055_GYRO_I2C_REG_HBW, BMX055_GYRO_HBW_DISABLE); - if (ret < 0) { - goto fail; - } - - // 116 Hz filter - ret = set_register(BMX055_GYRO_I2C_REG_BW, BMX055_GYRO_BW_116HZ); - if (ret < 0) { - goto fail; - } - - // +- 125 deg/s range - ret = set_register(BMX055_GYRO_I2C_REG_RANGE, BMX055_GYRO_RANGE_125); - if (ret < 0) { - goto fail; - } - - enabled = true; - -fail: - return ret; -} - -int BMX055_Gyro::shutdown() { - if (!enabled) return 0; - - // enter deep suspend mode (lowest power mode) - int ret = set_register(BMX055_GYRO_I2C_REG_LPM1, BMX055_GYRO_DEEP_SUSPEND); - if (ret < 0) { - LOGE("Could not move BMX055 GYRO in deep suspend mode!"); - } - - return ret; -} - -bool BMX055_Gyro::get_event(MessageBuilder &msg, uint64_t ts) { - uint64_t start_time = nanos_since_boot(); - uint8_t buffer[6]; - int len = read_register(BMX055_GYRO_I2C_REG_RATE_X_LSB, buffer, sizeof(buffer)); - assert(len == 6); - - // 16 bit = +- 125 deg/s - float scale = 125.0f / (1 << 15); - float x = -DEG2RAD(read_16_bit(buffer[0], buffer[1]) * scale); - float y = -DEG2RAD(read_16_bit(buffer[2], buffer[3]) * scale); - float z = DEG2RAD(read_16_bit(buffer[4], buffer[5]) * scale); - - auto event = msg.initEvent().initGyroscope2(); - event.setSource(cereal::SensorEventData::SensorSource::BMX055); - event.setVersion(1); - event.setSensor(SENSOR_GYRO_UNCALIBRATED); - event.setType(SENSOR_TYPE_GYROSCOPE_UNCALIBRATED); - event.setTimestamp(start_time); - - float xyz[] = {x, y, z}; - auto svec = event.initGyroUncalibrated(); - svec.setV(xyz); - svec.setStatus(true); - - return true; -} diff --git a/system/sensord/sensors/bmx055_gyro.h b/system/sensord/sensors/bmx055_gyro.h deleted file mode 100644 index 7be3e56563..0000000000 --- a/system/sensord/sensors/bmx055_gyro.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define BMX055_GYRO_I2C_ADDR 0x68 - -// Registers of the chip -#define BMX055_GYRO_I2C_REG_ID 0x00 -#define BMX055_GYRO_I2C_REG_RATE_X_LSB 0x02 -#define BMX055_GYRO_I2C_REG_RANGE 0x0F -#define BMX055_GYRO_I2C_REG_BW 0x10 -#define BMX055_GYRO_I2C_REG_LPM1 0x11 -#define BMX055_GYRO_I2C_REG_HBW 0x13 -#define BMX055_GYRO_I2C_REG_FIFO 0x3F - -// Constants -#define BMX055_GYRO_CHIP_ID 0x0F - -#define BMX055_GYRO_HBW_ENABLE 0b10000000 -#define BMX055_GYRO_HBW_DISABLE 0b00000000 -#define BMX055_GYRO_DEEP_SUSPEND 0b00100000 -#define BMX055_GYRO_NORMAL_MODE 0b00000000 - -#define BMX055_GYRO_RANGE_2000 0b000 -#define BMX055_GYRO_RANGE_1000 0b001 -#define BMX055_GYRO_RANGE_500 0b010 -#define BMX055_GYRO_RANGE_250 0b011 -#define BMX055_GYRO_RANGE_125 0b100 - -#define BMX055_GYRO_BW_116HZ 0b0010 - - -class BMX055_Gyro : public I2CSensor { - uint8_t get_device_address() {return BMX055_GYRO_I2C_ADDR;} -public: - BMX055_Gyro(I2CBus *bus); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown(); -}; diff --git a/system/sensord/sensors/bmx055_magn.cc b/system/sensord/sensors/bmx055_magn.cc deleted file mode 100644 index b498c5fe3d..0000000000 --- a/system/sensord/sensors/bmx055_magn.cc +++ /dev/null @@ -1,258 +0,0 @@ -#include "system/sensord/sensors/bmx055_magn.h" - -#include - -#include -#include -#include - -#include "common/swaglog.h" -#include "common/util.h" - -static int16_t compensate_x(trim_data_t trim_data, int16_t mag_data_x, uint16_t data_rhall) { - uint16_t process_comp_x0 = data_rhall; - int32_t process_comp_x1 = ((int32_t)trim_data.dig_xyz1) * 16384; - uint16_t process_comp_x2 = ((uint16_t)(process_comp_x1 / process_comp_x0)) - ((uint16_t)0x4000); - int16_t retval = ((int16_t)process_comp_x2); - int32_t process_comp_x3 = (((int32_t)retval) * ((int32_t)retval)); - int32_t process_comp_x4 = (((int32_t)trim_data.dig_xy2) * (process_comp_x3 / 128)); - int32_t process_comp_x5 = (int32_t)(((int16_t)trim_data.dig_xy1) * 128); - int32_t process_comp_x6 = ((int32_t)retval) * process_comp_x5; - int32_t process_comp_x7 = (((process_comp_x4 + process_comp_x6) / 512) + ((int32_t)0x100000)); - int32_t process_comp_x8 = ((int32_t)(((int16_t)trim_data.dig_x2) + ((int16_t)0xA0))); - int32_t process_comp_x9 = ((process_comp_x7 * process_comp_x8) / 4096); - int32_t process_comp_x10 = ((int32_t)mag_data_x) * process_comp_x9; - retval = ((int16_t)(process_comp_x10 / 8192)); - retval = (retval + (((int16_t)trim_data.dig_x1) * 8)) / 16; - - return retval; -} - -static int16_t compensate_y(trim_data_t trim_data, int16_t mag_data_y, uint16_t data_rhall) { - uint16_t process_comp_y0 = trim_data.dig_xyz1; - int32_t process_comp_y1 = (((int32_t)trim_data.dig_xyz1) * 16384) / process_comp_y0; - uint16_t process_comp_y2 = ((uint16_t)process_comp_y1) - ((uint16_t)0x4000); - int16_t retval = ((int16_t)process_comp_y2); - int32_t process_comp_y3 = ((int32_t) retval) * ((int32_t)retval); - int32_t process_comp_y4 = ((int32_t)trim_data.dig_xy2) * (process_comp_y3 / 128); - int32_t process_comp_y5 = ((int32_t)(((int16_t)trim_data.dig_xy1) * 128)); - int32_t process_comp_y6 = ((process_comp_y4 + (((int32_t)retval) * process_comp_y5)) / 512); - int32_t process_comp_y7 = ((int32_t)(((int16_t)trim_data.dig_y2) + ((int16_t)0xA0))); - int32_t process_comp_y8 = (((process_comp_y6 + ((int32_t)0x100000)) * process_comp_y7) / 4096); - int32_t process_comp_y9 = (((int32_t)mag_data_y) * process_comp_y8); - retval = (int16_t)(process_comp_y9 / 8192); - retval = (retval + (((int16_t)trim_data.dig_y1) * 8)) / 16; - - return retval; -} - -static int16_t compensate_z(trim_data_t trim_data, int16_t mag_data_z, uint16_t data_rhall) { - int16_t process_comp_z0 = ((int16_t)data_rhall) - ((int16_t) trim_data.dig_xyz1); - int32_t process_comp_z1 = (((int32_t)trim_data.dig_z3) * ((int32_t)(process_comp_z0))) / 4; - int32_t process_comp_z2 = (((int32_t)(mag_data_z - trim_data.dig_z4)) * 32768); - int32_t process_comp_z3 = ((int32_t)trim_data.dig_z1) * (((int16_t)data_rhall) * 2); - int16_t process_comp_z4 = (int16_t)((process_comp_z3 + (32768)) / 65536); - int32_t retval = ((process_comp_z2 - process_comp_z1) / (trim_data.dig_z2 + process_comp_z4)); - - /* saturate result to +/- 2 micro-tesla */ - retval = std::clamp(retval, -32767, 32767); - - /* Conversion of LSB to micro-tesla*/ - retval = retval / 16; - - return (int16_t)retval; -} - -BMX055_Magn::BMX055_Magn(I2CBus *bus) : I2CSensor(bus) {} - -int BMX055_Magn::init() { - uint8_t trim_x1y1[2] = {0}; - uint8_t trim_x2y2[2] = {0}; - uint8_t trim_xy1xy2[2] = {0}; - uint8_t trim_z1[2] = {0}; - uint8_t trim_z2[2] = {0}; - uint8_t trim_z3[2] = {0}; - uint8_t trim_z4[2] = {0}; - uint8_t trim_xyz1[2] = {0}; - - // suspend -> sleep - int ret = set_register(BMX055_MAGN_I2C_REG_PWR_0, 0x01); - if (ret < 0) { - LOGD("Enabling power failed: %d", ret); - goto fail; - } - util::sleep_for(5); // wait until the chip is powered on - - ret = verify_chip_id(BMX055_MAGN_I2C_REG_ID, {BMX055_MAGN_CHIP_ID}); - if (ret == -1) { - goto fail; - } - - // Load magnetometer trim - ret = read_register(BMX055_MAGN_I2C_REG_DIG_X1, trim_x1y1, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_X2, trim_x2y2, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_XY2, trim_xy1xy2, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z1_LSB, trim_z1, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z2_LSB, trim_z2, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z3_LSB, trim_z3, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_Z4_LSB, trim_z4, 2); - if (ret < 0) goto fail; - ret = read_register(BMX055_MAGN_I2C_REG_DIG_XYZ1_LSB, trim_xyz1, 2); - if (ret < 0) goto fail; - - // Read trim data - trim_data.dig_x1 = trim_x1y1[0]; - trim_data.dig_y1 = trim_x1y1[1]; - - trim_data.dig_x2 = trim_x2y2[0]; - trim_data.dig_y2 = trim_x2y2[1]; - - trim_data.dig_xy1 = trim_xy1xy2[1]; // NB: MSB/LSB swapped - trim_data.dig_xy2 = trim_xy1xy2[0]; - - trim_data.dig_z1 = read_16_bit(trim_z1[0], trim_z1[1]); - trim_data.dig_z2 = read_16_bit(trim_z2[0], trim_z2[1]); - trim_data.dig_z3 = read_16_bit(trim_z3[0], trim_z3[1]); - trim_data.dig_z4 = read_16_bit(trim_z4[0], trim_z4[1]); - - trim_data.dig_xyz1 = read_16_bit(trim_xyz1[0], trim_xyz1[1] & 0x7f); - assert(trim_data.dig_xyz1 != 0); - - perform_self_test(); - - // f_max = 1 / (145us * nXY + 500us * NZ + 980us) - // Chose NXY = 7, NZ = 12, which gives 125 Hz, - // and has the same ratio as the high accuracy preset - ret = set_register(BMX055_MAGN_I2C_REG_REPXY, (7 - 1) / 2); - if (ret < 0) { - goto fail; - } - - ret = set_register(BMX055_MAGN_I2C_REG_REPZ, 12 - 1); - if (ret < 0) { - goto fail; - } - - enabled = true; - return 0; - - fail: - return ret; -} - -int BMX055_Magn::shutdown() { - if (!enabled) return 0; - - // move to suspend mode - int ret = set_register(BMX055_MAGN_I2C_REG_PWR_0, 0); - if (ret < 0) { - LOGE("Could not move BMX055 MAGN in suspend mode!"); - } - - return ret; -} - -bool BMX055_Magn::perform_self_test() { - uint8_t buffer[8]; - int16_t x, y; - int16_t neg_z, pos_z; - - // Increase z reps for less false positives (~30 Hz ODR) - set_register(BMX055_MAGN_I2C_REG_REPXY, 1); - set_register(BMX055_MAGN_I2C_REG_REPZ, 64 - 1); - - // Clean existing measurement - read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer)); - - uint8_t forced = BMX055_MAGN_FORCED; - - // Negative current - set_register(BMX055_MAGN_I2C_REG_MAG, forced | (uint8_t(0b10) << 6)); - util::sleep_for(100); - - read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer)); - parse_xyz(buffer, &x, &y, &neg_z); - - // Positive current - set_register(BMX055_MAGN_I2C_REG_MAG, forced | (uint8_t(0b11) << 6)); - util::sleep_for(100); - - read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer)); - parse_xyz(buffer, &x, &y, &pos_z); - - // Put back in normal mode - set_register(BMX055_MAGN_I2C_REG_MAG, 0); - - int16_t diff = pos_z - neg_z; - bool passed = (diff > 180) && (diff < 240); - - if (!passed) { - LOGE("self test failed: neg %d pos %d diff %d", neg_z, pos_z, diff); - } - - return passed; -} - -bool BMX055_Magn::parse_xyz(uint8_t buffer[8], int16_t *x, int16_t *y, int16_t *z) { - bool ready = buffer[6] & 0x1; - if (ready) { - int16_t mdata_x = (int16_t) (((int16_t)buffer[1] << 8) | buffer[0]) >> 3; - int16_t mdata_y = (int16_t) (((int16_t)buffer[3] << 8) | buffer[2]) >> 3; - int16_t mdata_z = (int16_t) (((int16_t)buffer[5] << 8) | buffer[4]) >> 1; - uint16_t data_r = (uint16_t) (((uint16_t)buffer[7] << 8) | buffer[6]) >> 2; - assert(data_r != 0); - - *x = compensate_x(trim_data, mdata_x, data_r); - *y = compensate_y(trim_data, mdata_y, data_r); - *z = compensate_z(trim_data, mdata_z, data_r); - } - return ready; -} - - -bool BMX055_Magn::get_event(MessageBuilder &msg, uint64_t ts) { - uint64_t start_time = nanos_since_boot(); - uint8_t buffer[8]; - int16_t _x, _y, x, y, z; - - int len = read_register(BMX055_MAGN_I2C_REG_DATAX_LSB, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - bool parsed = parse_xyz(buffer, &_x, &_y, &z); - if (parsed) { - - auto event = msg.initEvent().initMagnetometer(); - event.setSource(cereal::SensorEventData::SensorSource::BMX055); - event.setVersion(2); - event.setSensor(SENSOR_MAGNETOMETER_UNCALIBRATED); - event.setType(SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED); - event.setTimestamp(start_time); - - // Move magnetometer into same reference frame as accel/gryo - x = -_y; - y = _x; - - // Axis convention - x = -x; - y = -y; - - float xyz[] = {(float)x, (float)y, (float)z}; - auto svec = event.initMagneticUncalibrated(); - svec.setV(xyz); - svec.setStatus(true); - } - - // The BMX055 Magnetometer has no FIFO mode. Self running mode only goes - // up to 30 Hz. Therefore we put in forced mode, and request measurements - // at a 100 Hz. When reading the registers we have to check the ready bit - // To verify the measurement was completed this cycle. - set_register(BMX055_MAGN_I2C_REG_MAG, BMX055_MAGN_FORCED); - - return parsed; -} diff --git a/system/sensord/sensors/bmx055_magn.h b/system/sensord/sensors/bmx055_magn.h deleted file mode 100644 index 15c4e734b9..0000000000 --- a/system/sensord/sensors/bmx055_magn.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define BMX055_MAGN_I2C_ADDR 0x10 - -// Registers of the chip -#define BMX055_MAGN_I2C_REG_ID 0x40 -#define BMX055_MAGN_I2C_REG_PWR_0 0x4B -#define BMX055_MAGN_I2C_REG_MAG 0x4C -#define BMX055_MAGN_I2C_REG_DATAX_LSB 0x42 -#define BMX055_MAGN_I2C_REG_RHALL_LSB 0x48 -#define BMX055_MAGN_I2C_REG_REPXY 0x51 -#define BMX055_MAGN_I2C_REG_REPZ 0x52 - -#define BMX055_MAGN_I2C_REG_DIG_X1 0x5D -#define BMX055_MAGN_I2C_REG_DIG_Y1 0x5E -#define BMX055_MAGN_I2C_REG_DIG_Z4_LSB 0x62 -#define BMX055_MAGN_I2C_REG_DIG_Z4_MSB 0x63 -#define BMX055_MAGN_I2C_REG_DIG_X2 0x64 -#define BMX055_MAGN_I2C_REG_DIG_Y2 0x65 -#define BMX055_MAGN_I2C_REG_DIG_Z2_LSB 0x68 -#define BMX055_MAGN_I2C_REG_DIG_Z2_MSB 0x69 -#define BMX055_MAGN_I2C_REG_DIG_Z1_LSB 0x6A -#define BMX055_MAGN_I2C_REG_DIG_Z1_MSB 0x6B -#define BMX055_MAGN_I2C_REG_DIG_XYZ1_LSB 0x6C -#define BMX055_MAGN_I2C_REG_DIG_XYZ1_MSB 0x6D -#define BMX055_MAGN_I2C_REG_DIG_Z3_LSB 0x6E -#define BMX055_MAGN_I2C_REG_DIG_Z3_MSB 0x6F -#define BMX055_MAGN_I2C_REG_DIG_XY2 0x70 -#define BMX055_MAGN_I2C_REG_DIG_XY1 0x71 - -// Constants -#define BMX055_MAGN_CHIP_ID 0x32 -#define BMX055_MAGN_FORCED (0b01 << 1) - -struct trim_data_t { - int8_t dig_x1; - int8_t dig_y1; - int8_t dig_x2; - int8_t dig_y2; - uint16_t dig_z1; - int16_t dig_z2; - int16_t dig_z3; - int16_t dig_z4; - uint8_t dig_xy1; - int8_t dig_xy2; - uint16_t dig_xyz1; -}; - - -class BMX055_Magn : public I2CSensor{ - uint8_t get_device_address() {return BMX055_MAGN_I2C_ADDR;} - trim_data_t trim_data = {0}; - bool perform_self_test(); - bool parse_xyz(uint8_t buffer[8], int16_t *x, int16_t *y, int16_t *z); -public: - BMX055_Magn(I2CBus *bus); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown(); -}; diff --git a/system/sensord/sensors/bmx055_temp.cc b/system/sensord/sensors/bmx055_temp.cc deleted file mode 100644 index da7b86476c..0000000000 --- a/system/sensord/sensors/bmx055_temp.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include "system/sensord/sensors/bmx055_temp.h" - -#include - -#include "system/sensord/sensors/bmx055_accel.h" -#include "common/swaglog.h" -#include "common/timing.h" - -BMX055_Temp::BMX055_Temp(I2CBus *bus) : I2CSensor(bus) {} - -int BMX055_Temp::init() { - return verify_chip_id(BMX055_ACCEL_I2C_REG_ID, {BMX055_ACCEL_CHIP_ID}) == -1 ? -1 : 0; -} - -bool BMX055_Temp::get_event(MessageBuilder &msg, uint64_t ts) { - uint64_t start_time = nanos_since_boot(); - uint8_t buffer[1]; - int len = read_register(BMX055_ACCEL_I2C_REG_TEMP, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - float temp = 23.0f + int8_t(buffer[0]) / 2.0f; - - auto event = msg.initEvent().initTemperatureSensor(); - event.setSource(cereal::SensorEventData::SensorSource::BMX055); - event.setVersion(1); - event.setType(SENSOR_TYPE_AMBIENT_TEMPERATURE); - event.setTimestamp(start_time); - event.setTemperature(temp); - - return true; -} diff --git a/system/sensord/sensors/bmx055_temp.h b/system/sensord/sensors/bmx055_temp.h deleted file mode 100644 index a2eabae395..0000000000 --- a/system/sensord/sensors/bmx055_temp.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "system/sensord/sensors/bmx055_accel.h" -#include "system/sensord/sensors/i2c_sensor.h" - -class BMX055_Temp : public I2CSensor { - uint8_t get_device_address() {return BMX055_ACCEL_I2C_ADDR;} -public: - BMX055_Temp(I2CBus *bus); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown() { return 0; } -}; diff --git a/system/sensord/sensors_qcom2.cc b/system/sensord/sensors_qcom2.cc index f9f51539c9..8ff9331b39 100644 --- a/system/sensord/sensors_qcom2.cc +++ b/system/sensord/sensors_qcom2.cc @@ -14,10 +14,6 @@ #include "common/swaglog.h" #include "common/timing.h" #include "common/util.h" -#include "system/sensord/sensors/bmx055_accel.h" -#include "system/sensord/sensors/bmx055_gyro.h" -#include "system/sensord/sensors/bmx055_magn.h" -#include "system/sensord/sensors/bmx055_temp.h" #include "system/sensord/sensors/constants.h" #include "system/sensord/sensors/lsm6ds3_accel.h" #include "system/sensord/sensors/lsm6ds3_gyro.h" @@ -117,11 +113,6 @@ void polling_loop(Sensor *sensor, std::string msg_name) { int sensor_loop(I2CBus *i2c_bus_imu) { // Sensor init std::vector> sensors_init = { - {new BMX055_Accel(i2c_bus_imu), "accelerometer2"}, - {new BMX055_Gyro(i2c_bus_imu), "gyroscope2"}, - {new BMX055_Magn(i2c_bus_imu), "magnetometer"}, - {new BMX055_Temp(i2c_bus_imu), "temperatureSensor2"}, - {new LSM6DS3_Accel(i2c_bus_imu, GPIO_LSM_INT), "accelerometer"}, {new LSM6DS3_Gyro(i2c_bus_imu, GPIO_LSM_INT, true), "gyroscope"}, {new LSM6DS3_Temp(i2c_bus_imu), "temperatureSensor"}, diff --git a/system/sensord/tests/test_sensord.py b/system/sensord/tests/test_sensord.py index 15f1f2dc35..1dab652386 100644 --- a/system/sensord/tests/test_sensord.py +++ b/system/sensord/tests/test_sensord.py @@ -12,13 +12,6 @@ from openpilot.common.timeout import Timeout from openpilot.system.hardware import HARDWARE from openpilot.system.manager.process_config import managed_processes -BMX = { - ('bmx055', 'acceleration'), - ('bmx055', 'gyroUncalibrated'), - ('bmx055', 'magneticUncalibrated'), - ('bmx055', 'temperature'), -} - LSM = { ('lsm6ds3', 'acceleration'), ('lsm6ds3', 'gyroUncalibrated'), @@ -30,17 +23,11 @@ MMC = { ('mmc5603nj', 'magneticUncalibrated'), } -SENSOR_CONFIGURATIONS: list[set] = [ - BMX | LSM, - MMC | LSM, - BMX | LSM_C, - MMC| LSM_C, -] -if HARDWARE.get_device_type() == "mici": - SENSOR_CONFIGURATIONS = [ - LSM, - LSM_C, - ] +SENSOR_CONFIGURATIONS: list[set] = { + "mici": [LSM, LSM_C], + "tizi": [MMC | LSM, MMC | LSM_C], + "tici": [LSM, LSM_C, MMC | LSM, MMC | LSM_C], +}.get(HARDWARE.get_device_type(), []) Sensor = log.SensorEventData.SensorSource SensorConfig = namedtuple('SensorConfig', ['type', 'sanity_min', 'sanity_max']) @@ -57,13 +44,6 @@ ALL_SENSORS = { SensorConfig("temperature", 0, 60), }, - Sensor.bmx055: { - SensorConfig("acceleration", 5, 15), - SensorConfig("gyroUncalibrated", 0, .2), - SensorConfig("magneticUncalibrated", 0, 300), - SensorConfig("temperature", 0, 60), - }, - Sensor.mmc5603nj: { SensorConfig("magneticUncalibrated", 0, 300), } diff --git a/system/sensord/tests/ttff_test.py b/system/sensord/tests/ttff_test.py deleted file mode 100755 index a9cc16d707..0000000000 --- a/system/sensord/tests/ttff_test.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 - -import time -import atexit - -from cereal import messaging -from openpilot.system.manager.process_config import managed_processes - -TIMEOUT = 10*60 - -def kill(): - for proc in ['ubloxd', 'pigeond']: - managed_processes[proc].stop(retry=True, block=True) - -if __name__ == "__main__": - # start ubloxd - managed_processes['ubloxd'].start() - atexit.register(kill) - - sm = messaging.SubMaster(['ubloxGnss']) - - times = [] - for i in range(20): - # start pigeond - st = time.monotonic() - managed_processes['pigeond'].start() - - # wait for a >4 satellite fix - while True: - sm.update(0) - msg = sm['ubloxGnss'] - if msg.which() == 'measurementReport' and sm.updated["ubloxGnss"]: - report = msg.measurementReport - if report.numMeas > 4: - times.append(time.monotonic() - st) - print(f"\033[94m{i}: Got a fix in {round(times[-1], 2)} seconds\033[0m") - break - - if time.monotonic() - st > TIMEOUT: - raise TimeoutError("\033[91mFailed to get a fix in {TIMEOUT} seconds!\033[0m") - - time.sleep(0.1) - - # stop pigeond - managed_processes['pigeond'].stop(retry=True, block=True) - time.sleep(20) - - print(f"\033[92mAverage TTFF: {round(sum(times) / len(times), 2)}s\033[0m") From c48f161b046ccb7bd1d982fd998205cd04c32438 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 26 May 2025 18:54:34 -0700 Subject: [PATCH 343/477] bump tinygrad (#35355) --- tinygrad_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinygrad_repo b/tinygrad_repo index 519dec6677..76eb130d8c 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 519dec6677f98718ee4f2d07be1936eb91dde73b +Subproject commit 76eb130d8c6567679641ff53a7215d1f6f88eb9a From 6a38dd13154542dcb337bb876086efed57e547e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 19:44:21 -0700 Subject: [PATCH 344/477] [bot] Update translations (#35354) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 9 +-------- selfdrive/ui/translations/main_de.ts | 9 +-------- selfdrive/ui/translations/main_es.ts | 9 +-------- selfdrive/ui/translations/main_fr.ts | 9 +-------- selfdrive/ui/translations/main_ja.ts | 9 +-------- selfdrive/ui/translations/main_ko.ts | 9 +-------- selfdrive/ui/translations/main_pt-BR.ts | 11 ++--------- selfdrive/ui/translations/main_th.ts | 9 +-------- selfdrive/ui/translations/main_tr.ts | 9 +-------- selfdrive/ui/translations/main_zh-CHS.ts | 9 +-------- selfdrive/ui/translations/main_zh-CHT.ts | 9 +-------- 11 files changed, 12 insertions(+), 89 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 603d6a01cd..0c3b934dde 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -400,13 +400,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - جارٍ التثبيت... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index aa6cf75c30..db0fe97036 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -392,13 +392,6 @@ Der Firehose-Modus ermöglicht es dir, deine Trainingsdaten-Uploads zu maximiere - - Installer - - Installing... - Installiere... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 64272244ea..c4ead7ca9d 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -392,13 +392,6 @@ El Modo Firehose te permite maximizar las subidas de datos de entrenamiento para - - Installer - - Installing... - Instalando... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index e7c4fa506a..74849bd6fc 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -390,13 +390,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - Installation... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 8fff080283..85d2494dfe 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -390,13 +390,6 @@ Firehoseモードを有効にすると、学習データを最大限アップロ - - Installer - - Installing... - インストール中... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 268475f917..dea01b44ee 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -390,13 +390,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - 설치 중... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 2a7d270713..f98f32b9a8 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -103,8 +103,8 @@ Rede Wi-Fi com Franquia - Prevent large data uploads when on a metered Wi-FI connection - Previna o envio de grandes volumes de dados em conexões Wi-Fi com franquia de limite de dados + Prevent large data uploads when on a metered Wi-Fi connection + @@ -392,13 +392,6 @@ O Modo Firehose permite maximizar o envio de dados de treinamento para melhorar - - Installer - - Installing... - Instalando... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index e04cdac3e3..c587cfbd12 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -390,13 +390,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - กำลังติดตั้ง... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 181c9c44f3..62de11145a 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -388,13 +388,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - Yükleniyor... - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 03e7a960be..26537d3f64 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -390,13 +390,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - 正在安装…… - - MultiOptionDialog diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index e28ac44b98..160c293072 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -103,7 +103,7 @@ - Prevent large data uploads when on a metered Wi-FI connection + Prevent large data uploads when on a metered Wi-Fi connection @@ -390,13 +390,6 @@ Firehose Mode allows you to maximize your training data uploads to improve openp - - Installer - - Installing... - 安裝中… - - MultiOptionDialog From 7511983ccb2746afe5c35d00cd2e7ead8aa55cf0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 28 May 2025 03:47:58 +0800 Subject: [PATCH 345/477] system/ui: cache shader location (#35360) cache shader location --- system/ui/widgets/cameraview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index bff13a7910..49832444f8 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -60,6 +60,7 @@ class CameraView: self._texture_needs_update = True self.last_connection_attempt: float = 0.0 self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER) + self._texture1_loc: int = rl.get_shader_location(self.shader, "texture1") if not TICI else -1 self.frame: VisionBuf | None = None self.texture_y: rl.Texture | None = None @@ -188,7 +189,7 @@ class CameraView: # Render with shader rl.begin_shader_mode(self.shader) - rl.set_shader_value_texture(self.shader, rl.get_shader_location(self.shader, "texture1"), self.texture_uv) + rl.set_shader_value_texture(self.shader, self._texture1_loc, self.texture_uv) rl.draw_texture_pro(self.texture_y, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) rl.end_shader_mode() From 28da563386c3bc4a480d441c95ca4982a70815ee Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 28 May 2025 03:48:33 +0800 Subject: [PATCH 346/477] system/ui: render model output with new ModelRenderer class (#35356) render model output with new ModelRenderer class --- system/ui/onroad/augmented_road_view.py | 15 +- system/ui/onroad/model_renderer.py | 360 ++++++++++++++++++++++++ 2 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 system/ui/onroad/model_renderer.py diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 7ae8f37c13..316167a828 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -3,6 +3,7 @@ import pyray as rl from cereal import messaging, log from msgq.visionipc import VisionStreamType +from openpilot.system.ui.onroad.model_renderer import ModelRenderer from openpilot.system.ui.widgets.cameraview import CameraView from openpilot.system.ui.lib.application import gui_app from openpilot.common.transformations.camera import DEVICE_CAMERAS, DeviceCameraConfig, view_frame_from_device_frame @@ -29,6 +30,8 @@ class AugmentedRoadView(CameraView): self._last_rect_dims = (0.0, 0.0) self._cached_matrix: np.ndarray | None = None + self.model_renderer = ModelRenderer() + def render(self, rect): # Update calibration before rendering self._update_calibration() @@ -41,6 +44,7 @@ class AugmentedRoadView(CameraView): # - Path prediction # - Lead vehicle indicators # - Additional features + self.model_renderer.draw(rect, self.sm) def _update_calibration(self): # Update device camera if not already set @@ -112,12 +116,21 @@ class AugmentedRoadView(CameraView): [0, 0, 1.0] ]) + video_transform = np.array([ + [zoom, 0.0, (w / 2 - x_offset) - (cx * zoom)], + [0.0, zoom, (h / 2 - y_offset) - (cy * zoom)], + [0.0, 0.0, 1.0] + ]) + self.model_renderer.set_transform(video_transform @ calib_transform) + return self._cached_matrix if __name__ == "__main__": gui_app.init_window("OnRoad Camera View") - sm = messaging.SubMaster(['deviceState', 'liveCalibration', 'roadCameraState']) + sm = messaging.SubMaster(["modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", + "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", + "roadCameraState", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan"]) road_camera_view = AugmentedRoadView(sm, VisionStreamType.VISION_STREAM_ROAD) try: for _ in gui_app.render(): diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py new file mode 100644 index 0000000000..d0b66666fa --- /dev/null +++ b/system/ui/onroad/model_renderer.py @@ -0,0 +1,360 @@ +import colorsys +import bisect +import numpy as np +import pyray as rl +from cereal import messaging, car +from openpilot.common.params import Params + + +CLIP_MARGIN = 500 +MIN_DRAW_DISTANCE = 10.0 +MAX_DRAW_DISTANCE = 100.0 + + +THROTTLE_COLORS = [ + rl.Color(0, 231, 130, 102), # Green with alpha 0.4 + rl.Color(112, 247, 35, 89), # Lime with alpha 0.35 + rl.Color(112, 247, 35, 0), # Transparent lime +] + +NO_THROTTLE_COLORS = [ + rl.Color(242, 242, 242, 102), # Light gray with alpha 0.4 + rl.Color(242, 242, 242, 89), # Light gray with alpha 0.35 + rl.Color(242, 242, 242, 0), # Transparent light gray +] + + +class ModelRenderer: + def __init__(self): + self._longitudinal_control = False + self._experimental_mode = False + self._blend_factor = 1.0 + self._prev_allow_throttle = True + self._lane_line_probs = [0.0] * 4 + self._road_edge_stds = [0.0] * 2 + self._path_offset_z = 1.22 + + # Initialize empty polygon vertices + self._track_vertices = [] + self._lane_line_vertices = [[] for _ in range(4)] + self._road_edge_vertices = [[] for _ in range(2)] + self._lead_vertices = [None, None] + + # Transform matrix (3x3 for car space to screen space) + self._car_space_transform = np.zeros((3, 3)) + self._transform_dirty = True + self._clip_region = None + + # Get longitudinal control setting from car parameters + car_params = Params().get("CarParams") + if car_params: + cp = messaging.log_from_bytes(car_params, car.CarParams) + self._longitudinal_control = cp.openpilotLongitudinalControl + + def set_transform(self, transform: np.ndarray): + self._car_space_transform = transform + self._transform_dirty = True + + def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster): + # Check if data is up-to-date + if not sm.valid['modelV2'] or not sm.valid['liveCalibration']: + return + + # Set up clipping region + self._clip_region = rl.Rectangle( + rect.x - CLIP_MARGIN, rect.y - CLIP_MARGIN, rect.width + 2 * CLIP_MARGIN, rect.height + 2 * CLIP_MARGIN + ) + + # Update flags based on car state + self._experimental_mode = sm['selfdriveState'].experimentalMode + self._path_offset_z = sm['liveCalibration'].height[0] + if sm.updated['carParams']: + self._longitudinal_control = sm['carParams'].openpilotLongitudinalControl + + # Get model and radar data + model = sm['modelV2'] + radar_state = sm['radarState'] if sm.valid['radarState'] else None + lead_one = radar_state.leadOne if radar_state else None + render_lead_indicator = self._longitudinal_control and radar_state is not None + + # Update model data when needed + if self._transform_dirty or sm.updated['modelV2'] or sm.updated['radarState']: + self._update_model(model, lead_one) + if render_lead_indicator: + self._update_leads(radar_state, model.position) + self._transform_dirty = False + + # Draw elements + self._draw_lane_lines() + self._draw_path(sm, model, rect.height) + + # Draw lead vehicles if available + if render_lead_indicator and radar_state: + lead_two = radar_state.leadTwo + + if lead_one and lead_one.status: + self._draw_lead(lead_one, self._lead_vertices[0], rect) + + if lead_two and lead_two.status and lead_one and (abs(lead_one.dRel - lead_two.dRel) > 3.0): + self._draw_lead(lead_two, self._lead_vertices[1], rect) + + def _update_leads(self, radar_state, line): + """Update positions of lead vehicles""" + leads = [radar_state.leadOne, radar_state.leadTwo] + for i, lead_data in enumerate(leads): + if lead_data and lead_data.status: + d_rel = lead_data.dRel + y_rel = lead_data.yRel + idx = self._get_path_length_idx(line, d_rel) + z = line.z[idx] + self._lead_vertices[i] = self._map_to_screen(d_rel, -y_rel, z + self._path_offset_z) + + def _update_model(self, model, lead): + """Update model visualization data based on model message""" + model_position = model.position + + # Determine max distance to render + max_distance = np.clip(model_position.x[-1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE) + + # Update lane lines + lane_lines = model.laneLines + line_probs = model.laneLineProbs + max_idx = self._get_path_length_idx(lane_lines[0], max_distance) + + for i in range(4): + self._lane_line_probs[i] = line_probs[i] + self._lane_line_vertices[i] = self._map_line_to_polygon( + lane_lines[i], 0.025 * self._lane_line_probs[i], 0, max_idx + ) + + # Update road edges + road_edges = model.roadEdges + edge_stds = model.roadEdgeStds + + for i in range(2): + self._road_edge_stds[i] = edge_stds[i] + self._road_edge_vertices[i] = self._map_line_to_polygon(road_edges[i], 0.025, 0, max_idx) + + # Update path + if lead and lead.status: + lead_d = lead.dRel * 2.0 + max_distance = np.clip(lead_d - min(lead_d * 0.35, 10.0), 0.0, max_distance) + max_idx = self._get_path_length_idx(model_position, max_distance) + + self._track_vertices = self._map_line_to_polygon(model_position, 0.9, self._path_offset_z, max_idx, False) + + def _draw_lane_lines(self): + """Draw lane lines and road edges""" + for i in range(4): + # Skip if no vertices + if not self._lane_line_vertices[i]: + continue + + # Draw lane line + alpha = np.clip(self._lane_line_probs[i], 0.0, 0.7) + color = rl.Color(255, 255, 255, int(alpha * 255)) + self._draw_polygon(self._lane_line_vertices[i], color) + + for i in range(2): + # Skip if no vertices + if not self._road_edge_vertices[i]: + continue + + # Draw road edge + alpha = np.clip(1.0 - self._road_edge_stds[i], 0.0, 1.0) + color = rl.Color(255, 0, 0, int(alpha * 255)) + self._draw_polygon(self._road_edge_vertices[i], color) + + def _draw_path(self, sm, model, height): + """Draw the path polygon with gradient based on acceleration""" + if not self._track_vertices: + return + + if self._experimental_mode: + # Draw with acceleration coloring + acceleration = model.acceleration.x + max_len = min(len(self._track_vertices) // 2, len(acceleration)) + + # Create gradient colors for path sections + for i in range(max_len): + track_idx = max_len - i - 1 # flip idx to start from bottom right + track_y = self._track_vertices[track_idx][1] + # Skip points out of frame + if track_y < 0 or track_y > height: + continue + + # Calculate color based on acceleration + lin_grad_point = (height - track_y) / height + + # speed up: 120, slow down: 0 + path_hue = max(min(60 + acceleration[i] * 35, 120), 0) + path_hue = int(path_hue * 100 + 0.5) / 100 + + saturation = min(abs(acceleration[i] * 1.5), 1) + lightness = self._map_val(saturation, 0.0, 1.0, 0.95, 0.62) + alpha = self._map_val(lin_grad_point, 0.75 / 2.0, 0.75, 0.4, 0.0) + + # Use HSL to RGB conversion + color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha) + + # TODO: This is simplified - a full implementation would create a gradient fill + segment = self._track_vertices[track_idx : track_idx + 2] + self._track_vertices[-track_idx - 2 : -track_idx] + self._draw_polygon(segment, color) + + # Skip a point, unless next is last + i += 1 if i + 2 < max_len else 0 + else: + # Draw with throttle/no throttle gradient + allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control + + # Start transition if throttle state changes + if allow_throttle != self._prev_allow_throttle: + self._prev_allow_throttle = allow_throttle + self._blend_factor = max(1.0 - self._blend_factor, 0.0) + + # Update blend factor + if self._blend_factor < 1.0: + self._blend_factor = min(self._blend_factor + 0.1, 1.0) + + begin_colors = NO_THROTTLE_COLORS if allow_throttle else THROTTLE_COLORS + end_colors = THROTTLE_COLORS if allow_throttle else NO_THROTTLE_COLORS + + # Blend colors based on transition + colors = [ + self._blend_colors(begin_colors[0], end_colors[0], self._blend_factor), + self._blend_colors(begin_colors[1], end_colors[1], self._blend_factor), + self._blend_colors(begin_colors[2], end_colors[2], self._blend_factor), + ] + + self._draw_polygon(self._track_vertices, colors[0]) + + def _draw_lead(self, lead_data, vd, rect): + """Draw lead vehicle indicator""" + if not vd: + return + + speed_buff = 10.0 + lead_buff = 40.0 + d_rel = lead_data.dRel + v_rel = lead_data.vRel + + # Calculate fill alpha + fill_alpha = 0 + if d_rel < lead_buff: + fill_alpha = 255 * (1.0 - (d_rel / lead_buff)) + if v_rel < 0: + fill_alpha += 255 * (-1 * (v_rel / speed_buff)) + fill_alpha = min(fill_alpha, 255) + + # Calculate size and position + sz = np.clip((25 * 30) / (d_rel / 3 + 30), 15.0, 30.0) * 2.35 + x = np.clip(vd[0], 0.0, rect.width - sz / 2) + y = min(vd[1], rect.height - sz * 0.6) + + g_xo = sz / 5 + g_yo = sz / 10 + + # Draw glow + glow = [(x + (sz * 1.35) + g_xo, y + sz + g_yo), (x, y - g_yo), (x - (sz * 1.35) - g_xo, y + sz + g_yo)] + rl.draw_triangle_fan(glow, len(glow), rl.Color(218, 202, 37, 255)) + + # Draw chevron + chevron = [(x + (sz * 1.25), y + sz), (x, y), (x - (sz * 1.25), y + sz)] + rl.draw_triangle_fan(chevron, len(chevron), rl.Color(201, 34, 49, int(fill_alpha))) + + @staticmethod + def _get_path_length_idx(line, path_height): + """Get the index corresponding to the given path height""" + return bisect.bisect_right(line.x, path_height) - 1 + + def _map_to_screen(self, in_x, in_y, in_z): + """Project a point in car space to screen space""" + input_pt = np.array([in_x, in_y, in_z]) + pt = self._car_space_transform @ input_pt + + if abs(pt[2]) < 1e-6: + return None + + x = pt[0] / pt[2] + y = pt[1] / pt[2] + + clip = self._clip_region + if x < clip.x or x > clip.x + clip.width or y < clip.y or y > clip.y + clip.height: + return None + + return (x, y) + + def _map_line_to_polygon(self, line, y_off, z_off, max_idx, allow_invert=True): + """Convert a 3D line to a 2D polygon for drawing""" + line_x = line.x + line_y = line.y + line_z = line.z + + left_points = [] + right_points = [] + + for i in range(max_idx + 1): + # Skip points with negative x (behind camera) + if line_x[i] < 0: + continue + + left = self._map_to_screen(line_x[i], line_y[i] - y_off, line_z[i] + z_off) + right = self._map_to_screen(line_x[i], line_y[i] + y_off, line_z[i] + z_off) + + if left and right: + # Check for inversion when going over hills + if not allow_invert and left_points and left[1] > left_points[-1][1]: + continue + + left_points.append(left) + right_points.append(right) + + if not left_points: + return [] + + return left_points + right_points[::-1] + + def _draw_polygon(self, points, color): + # TODO: Enhance polygon drawing to support even-odd fill rule efficiently, as Raylib lacks native support. + # Use a faster triangulation algorithm (e.g., ear clipping) or GPU shader for + # efficient rendering of lane lines, road edges, and path polygons. + if len(points) <= 8: + rl.draw_triangle_fan(points, len(points), color) + else: + for i in range(1, len(points) - 1): + rl.draw_triangle(points[0], points[i], points[i + 1], color) + + for i in range(len(points)): + rl.draw_line_ex(points[i], points[(i + 1) % len(points)], 1.5, color) + + @staticmethod + def _map_val(x, x0, x1, y0, y1): + """Map value x from range [x0, x1] to range [y0, y1]""" + return y0 + (y1 - y0) * ((x - x0) / (x1 - x0)) if x1 != x0 else y0 + + @staticmethod + def _hsla_to_color(h, s, l, a): + """Convert HSLA color to Raylib Color using colorsys module""" + # colorsys uses HLS format (Hue, Lightness, Saturation) + r, g, b = colorsys.hls_to_rgb(h, l, s) + + # Ensure values are in valid range + r_val = max(0, min(255, int(r * 255))) + g_val = max(0, min(255, int(g * 255))) + b_val = max(0, min(255, int(b * 255))) + a_val = max(0, min(255, int(a * 255))) + + return rl.Color(r_val, g_val, b_val, a_val) + + @staticmethod + def _blend_colors(start, end, t): + """Blend between two colors with factor t""" + if t >= 1.0: + return end + + return rl.Color( + int((1 - t) * start.r + t * end.r), + int((1 - t) * start.g + t * end.g), + int((1 - t) * start.b + t * end.b), + int((1 - t) * start.a + t * end.a), + ) From 3a7f0b66aa502055ad035cbcd18d503d0198bb22 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 28 May 2025 06:01:53 +0800 Subject: [PATCH 347/477] system/ui: fix remaining issues in WiFi Manager (#35301) * WIP * fix callback * fix connecting network displayed as Connected * thread safe states * fix state sync issues * fix callback --- system/ui/lib/wifi_manager.py | 69 +++++++++++++------------ system/ui/widgets/network.py | 95 +++++++++++++++++++++-------------- 2 files changed, 93 insertions(+), 71 deletions(-) diff --git a/system/ui/lib/wifi_manager.py b/system/ui/lib/wifi_manager.py index 96726bd999..d8a8144fb0 100644 --- a/system/ui/lib/wifi_manager.py +++ b/system/ui/lib/wifi_manager.py @@ -69,8 +69,9 @@ class NetworkInfo: class WifiManagerCallbacks: need_auth: Callable[[str], None] | None = None activated: Callable[[], None] | None = None - forgotten: Callable[[], None] | None = None + forgotten: Callable[[str], None] | None = None networks_updated: Callable[[list[NetworkInfo]], None] | None = None + connection_failed: Callable[[str, str], None] | None = None # Added for error feedback class WifiManager: @@ -98,8 +99,8 @@ class WifiManager: self.bus = await MessageBus(bus_type=BusType.SYSTEM).connect() if not await self._find_wifi_device(): raise ValueError("No Wi-Fi device found") - await self._setup_signals(self.device_path) + await self._setup_signals(self.device_path) self.active_ap_path = await self.get_active_access_point() await self.add_tethering_connection(self._tethering_ssid, DEFAULT_TETHERING_PASSWORD) self.saved_connections = await self._get_saved_connections() @@ -122,7 +123,7 @@ class WifiManager: if self.bus: self.bus.disconnect() - async def request_scan(self) -> None: + async def _request_scan(self) -> None: try: interface = self.device_proxy.get_interface(NM_WIRELESS_IFACE) await interface.call_request_scan({}) @@ -146,12 +147,23 @@ class WifiManager: try: nm_iface = await self._get_interface(NM, path, NM_CONNECTION_IFACE) await nm_iface.call_delete() + if self._current_connection_ssid == ssid: self._current_connection_ssid = None if ssid in self.saved_connections: del self.saved_connections[ssid] + for network in self.networks: + if network.ssid == ssid: + network.is_saved = False + network.is_connected = False + break + + # Notify UI of forgotten connection + if self.callbacks.networks_updated: + self.callbacks.networks_updated(copy.deepcopy(self.networks)) + return True except DBusError as e: cloudlog.error(f"Failed to delete connection for SSID: {ssid}. Error: {e}") @@ -212,10 +224,12 @@ class WifiManager: nm_iface = await self._get_interface(NM, NM_PATH, NM_IFACE) await nm_iface.call_add_and_activate_connection(connection, self.device_path, "/") - await self._update_connection_status() - except DBusError as e: + except Exception as e: self._current_connection_ssid = None cloudlog.error(f"Error connecting to network: {e}") + # Notify UI of failure + if self.callbacks.connection_failed: + self.callbacks.connection_failed(ssid, str(e)) def is_saved(self, ssid: str) -> bool: return ssid in self.saved_connections @@ -393,8 +407,7 @@ class WifiManager: async def _periodic_scan(self): while self.running: try: - await self.request_scan() - await self._get_available_networks() + await self._request_scan() await asyncio.sleep(30) except asyncio.CancelledError: break @@ -423,21 +436,24 @@ class WifiManager: def _on_properties_changed(self, interface: str, changed: dict, invalidated: list): # print("property changed", interface, changed, invalidated) if 'LastScan' in changed: - asyncio.create_task(self._get_available_networks()) + asyncio.create_task(self._refresh_networks()) elif interface == NM_WIRELESS_IFACE and "ActiveAccessPoint" in changed: - self.active_ap_path = changed["ActiveAccessPoint"].value - asyncio.create_task(self._get_available_networks()) + new_ap_path = changed["ActiveAccessPoint"].value + if self.active_ap_path != new_ap_path: + self.active_ap_path = new_ap_path + asyncio.create_task(self._refresh_networks()) def _on_state_changed(self, new_state: int, old_state: int, reason: int): - print(f"State changed: {old_state} -> {new_state}, reason: {reason}") + print("State changed", new_state, old_state, reason) if new_state == NMDeviceState.ACTIVATED: if self.callbacks.activated: self.callbacks.activated() - asyncio.create_task(self._update_connection_status()) + asyncio.create_task(self._refresh_networks()) self._current_connection_ssid = None elif new_state in (NMDeviceState.DISCONNECTED, NMDeviceState.NEED_AUTH): for network in self.networks: network.is_connected = False + if new_state == NMDeviceState.NEED_AUTH and reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and self.callbacks.need_auth: if self._current_connection_ssid: self.callbacks.need_auth(self._current_connection_ssid) @@ -453,19 +469,19 @@ class WifiManager: def _on_new_connection(self, path: str) -> None: """Callback for NewConnection signal.""" - print(f"New connection added: {path}") asyncio.create_task(self._add_saved_connection(path)) def _on_connection_removed(self, path: str) -> None: """Callback for ConnectionRemoved signal.""" - print(f"Connection removed: {path}") for ssid, p in list(self.saved_connections.items()): if path == p: del self.saved_connections[ssid] + if self.callbacks.forgotten: - self.callbacks.forgotten() + self.callbacks.forgotten(ssid) + # Update network list to reflect the removed saved connection - asyncio.create_task(self._update_connection_status()) + asyncio.create_task(self._refresh_networks()) break async def _add_saved_connection(self, path: str) -> None: @@ -474,7 +490,7 @@ class WifiManager: settings = await self._get_connection_settings(path) if ssid := self._extract_ssid(settings): self.saved_connections[ssid] = path - await self._update_connection_status() + await self._refresh_networks() except DBusError as e: cloudlog.error(f"Failed to add connection {path}: {e}") @@ -483,10 +499,6 @@ class WifiManager: ssid_variant = settings.get('802-11-wireless', {}).get('ssid', Variant('ay', b'')).value return ''.join(chr(b) for b in ssid_variant) if ssid_variant else None - async def _update_connection_status(self): - self.active_ap_path = await self.get_active_access_point() - await self._get_available_networks() - async def _add_match_rule(self, rule): """Add a match rule on the bus.""" reply = await self.bus.call( @@ -504,10 +516,11 @@ class WifiManager: assert reply.message_type == MessageType.METHOD_RETURN return reply - async def _get_available_networks(self): + async def _refresh_networks(self): """Get a list of available networks via NetworkManager.""" wifi_iface = self.device_proxy.get_interface(NM_WIRELESS_IFACE) access_points = await wifi_iface.get_access_points() + self.active_ap_path = await self.get_active_access_point() network_dict = {} for ap_path in access_points: try: @@ -531,7 +544,7 @@ class WifiManager: security_type=self._get_security_type(flags, wpa_flags, rsn_flags), path=ap_path, bssid=bssid, - is_connected=self.active_ap_path == ap_path, + is_connected=self.active_ap_path == ap_path and self._current_connection_ssid != ssid, is_saved=ssid in self.saved_connections ) @@ -555,9 +568,7 @@ class WifiManager: async def _get_connection_settings(self, path): """Fetch connection settings for a specific connection path.""" try: - connection_proxy = await self.bus.introspect(NM, path) - connection = self.bus.get_proxy_object(NM, path, connection_proxy) - settings = connection.get_interface(NM_CONNECTION_IFACE) + settings = await self._get_interface(NM, path, NM_CONNECTION_IFACE) return await settings.call_get_settings() except DBusError as e: cloudlog.error(f"Failed to get settings for {path}: {str(e)}") @@ -660,12 +671,6 @@ class WifiManagerWrapper: return self._run_coroutine(self._manager.connect()) - def request_scan(self): - """Request a scan for Wi-Fi networks.""" - if not self._manager: - return - self._run_coroutine(self._manager.request_scan()) - def forget_connection(self, ssid: str): """Forget a saved Wi-Fi connection.""" if not self._manager: diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 5877b3ff7a..46274dbf7e 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from threading import Lock from typing import Literal import pyray as rl @@ -53,48 +54,59 @@ UIState = StateIdle | StateConnecting | StateNeedsAuth | StateShowForgetConfirm class WifiManagerUI: def __init__(self, wifi_manager: WifiManagerWrapper): self.state: UIState = StateIdle() - self.btn_width = 200 + self.btn_width: int = 200 self.scroll_panel = GuiScrollPanel() self.keyboard = Keyboard(max_text_size=MAX_PASSWORD_LENGTH, min_text_size=MIN_PASSWORD_LENGTH, show_password_toggle=True) self._networks: list[NetworkInfo] = [] - + self._lock = Lock() self.wifi_manager = wifi_manager - self.wifi_manager.set_callbacks(WifiManagerCallbacks(self._on_need_auth, self._on_activated, self._on_forgotten, self._on_network_updated)) + + self.wifi_manager.set_callbacks( + WifiManagerCallbacks( + need_auth = self._on_need_auth, + activated = self._on_activated, + forgotten = self._on_forgotten, + networks_updated = self._on_network_updated, + connection_failed = self._on_connection_failed + ) + ) self.wifi_manager.start() self.wifi_manager.connect() def render(self, rect: rl.Rectangle): - if not self._networks: - gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) - return + with self._lock: + if not self._networks: + gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + return - match self.state: - case StateNeedsAuth(network): - result = self.keyboard.render("Enter password", f"for {network.ssid}") - if result == 1: - password = self.keyboard.text - self.keyboard.clear() + match self.state: + case StateNeedsAuth(network): + result = self.keyboard.render("Enter password", f"for {network.ssid}") + if result == 1: + password = self.keyboard.text + self.keyboard.clear() - if len(password) >= MIN_PASSWORD_LENGTH: - self.connect_to_network(network, password) - elif result == 0: - self.state = StateIdle() + if len(password) >= MIN_PASSWORD_LENGTH: + self.connect_to_network(network, password) + elif result == 0: + self.state = StateIdle() - case StateShowForgetConfirm(network): - result = confirm_dialog(f'Forget Wi-Fi Network "{network.ssid}"?', "Forget") - if result == 1: - self.forget_network(network) - elif result == 0: - self.state = StateIdle() + case StateShowForgetConfirm(network): + result = confirm_dialog(f'Forget Wi-Fi Network "{network.ssid}"?', "Forget") + if result == 1: + self.forget_network(network) + elif result == 0: + self.state = StateIdle() - case _: - self._draw_network_list(rect) + case _: + self._draw_network_list(rect) @property def require_full_screen(self) -> bool: """Check if the WiFi UI requires exclusive full-screen rendering.""" - return isinstance(self.state, (StateNeedsAuth, StateShowForgetConfirm)) + with self._lock: + return isinstance(self.state, (StateNeedsAuth, StateShowForgetConfirm)) def _draw_network_list(self, rect: rl.Rectangle): content_rect = rl.Rectangle(rect.x, rect.y, rect.width, len(self._networks) * ITEM_HEIGHT) @@ -190,25 +202,30 @@ class WifiManagerUI: self.wifi_manager.forget_connection(network.ssid) def _on_network_updated(self, networks: list[NetworkInfo]): - self._networks = networks + with self._lock: + self._networks = networks def _on_need_auth(self, ssid): - match self.state: - case StateConnecting(ssid): - self.state = StateNeedsAuth(ssid) - case _: - # Find network by SSID - network = next((n for n in self.wifi_manager.networks if n.ssid == ssid), None) - if network: - self.state = StateNeedsAuth(network) + with self._lock: + network = next((n for n in self._networks if n.ssid == ssid), None) + if network: + self.state = StateNeedsAuth(network) def _on_activated(self): - if isinstance(self.state, StateConnecting): - self.state = StateIdle() + with self._lock: + if isinstance(self.state, StateConnecting): + self.state = StateIdle() + + def _on_forgotten(self, ssid): + with self._lock: + if isinstance(self.state, StateForgetting): + self.state = StateIdle() + + def _on_connection_failed(self, ssid: str, error: str): + with self._lock: + if isinstance(self.state, StateConnecting): + self.state = StateIdle() - def _on_forgotten(self): - if isinstance(self.state, StateForgetting): - self.state = StateIdle() def main(): From 83ba27d7c01d1511a1def1f9e4d0214dca33b35f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 27 May 2025 19:42:02 -0700 Subject: [PATCH 348/477] Update lane departure warning toggle live (#35363) update LDWS live --- selfdrive/selfdrived/selfdrived.py | 1 + 1 file changed, 1 insertion(+) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 98c03acf1e..f3dabf3791 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -485,6 +485,7 @@ class SelfdriveD: def params_thread(self, evt): while not evt.is_set(): self.is_metric = self.params.get_bool("IsMetric") + self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl self.personality = self.read_personality_param() From b119006f6a67ba874ef6c718ac49c25deef06e29 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 27 May 2025 20:20:38 -0700 Subject: [PATCH 349/477] Cycle onroad when changing offroad-only toggles (#35361) * bad * Revert "bad" This reverts commit 6b5475dd90c3a29c00d946c94d726563cbec6179. * notify param * doesn't need to live in low level paramcontrol, rename param * should work * fix * disable while engaged * note * fix * just in case * param is cleared by manager -- this was all to ensure manager got our `started` transition * clean up * and that * rm * negative better than generic thing * timer is needed as it's not clean to fix case where you toggle while no ignition -- you can't go onroad + this allows some nice tolerance time for user to switch 2 toggles * these aren't required or useful * add to description * no longer unlive * allow reset button too * another pr -- Revert "allow reset button too" This reverts commit 5d03edddc80d8625ceba5d5178b2781e9d10d9c9. * rm space from i18n string * car is powered on --- common/params_keys.h | 1 + selfdrive/ui/qt/offroad/settings.cc | 27 +++++++++++++++++++++++---- selfdrive/ui/ui.cc | 5 +++++ selfdrive/ui/ui.h | 2 ++ system/hardware/hardwared.py | 11 ++++++++++- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index ca779a5b5c..3fd4e1b6ab 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -93,6 +93,7 @@ inline static std::unordered_map keys = { {"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START}, {"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START}, {"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START}, + {"OnroadCycleRequested", CLEAR_ON_MANAGER_START}, {"OpenpilotEnabledToggle", PERSISTENT}, {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index ed06734f0f..baac582645 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -17,49 +17,56 @@ #include "selfdrive/ui/qt/offroad/firehose.h" TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { - // param, title, desc, icon - std::vector> toggle_defs{ + // param, title, desc, icon, restart needed + std::vector> toggle_defs{ { "OpenpilotEnabledToggle", tr("Enable openpilot"), - tr("Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off."), + tr("Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature."), "../assets/icons/chffr_wheel.png", + true, }, { "ExperimentalMode", tr("Experimental Mode"), "", "../assets/icons/experimental_white.svg", + false, }, { "DisengageOnAccelerator", tr("Disengage on Accelerator Pedal"), tr("When enabled, pressing the accelerator pedal will disengage openpilot."), "../assets/icons/disengage_on_accelerator.svg", + false, }, { "IsLdwEnabled", tr("Enable Lane Departure Warnings"), tr("Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h)."), "../assets/icons/warning.png", + false, }, { "AlwaysOnDM", tr("Always-On Driver Monitoring"), tr("Enable driver monitoring even when openpilot is not engaged."), "../assets/icons/monitoring.png", + false, }, { "RecordFront", tr("Record and Upload Driver Camera"), tr("Upload data from the driver facing camera and help improve the driver monitoring algorithm."), "../assets/icons/monitoring.png", + true, }, { "IsMetric", tr("Use Metric System"), tr("Display speed in km/h instead of mph."), "../assets/icons/metric.png", + false, }, }; @@ -75,12 +82,24 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { // set up uiState update for personality setting QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState); - for (auto &[param, title, desc, icon] : toggle_defs) { + for (auto &[param, title, desc, icon, needs_restart] : toggle_defs) { auto toggle = new ParamControl(param, title, desc, icon, this); bool locked = params.getBool((param + "Lock").toStdString()); toggle->setEnabled(!locked); + if (needs_restart && !locked) { + toggle->setDescription(toggle->getDescription() + " " + tr("Changing this setting will restart openpilot if the car is powered on.")); + + QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) { + toggle->setEnabled(!engaged); + }); + + QObject::connect(toggle, &ParamControl::toggleFlipped, [=](bool state) { + params.putBool("OnroadCycleRequested", true); + }); + } + addItem(toggle); toggles[param.toStdString()] = toggle; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index ec3d40961d..79a245a0e7 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -78,6 +78,11 @@ void UIState::updateStatus() { } } + if (engaged() != engaged_prev) { + engaged_prev = engaged(); + emit engagedChanged(engaged()); + } + // Handle onroad/offroad transition if (scene.started != started_prev || sm->frame == 1) { if (scene.started) { diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 6ff67cacde..fd2aee771e 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -81,6 +81,7 @@ public: signals: void uiUpdate(const UIState &s); void offroadTransition(bool offroad); + void engagedChanged(bool engaged); private slots: void update(); @@ -88,6 +89,7 @@ private slots: private: QTimer *timer; bool started_prev = false; + bool engaged_prev = false; }; UIState *uiState(); diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index b6de91818e..15a191adc4 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -34,6 +34,7 @@ CURRENT_TAU = 15. # 15s time constant TEMP_TAU = 5. # 5s time constant DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect so you get an alert PANDA_STATES_TIMEOUT = round(1000 / SERVICE_LIST['pandaStates'].frequency * 1.5) # 1.5x the expected pandaState frequency +ONROAD_CYCLE_TIME = 1 # seconds to wait offroad after requesting an onroad cycle ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp']) HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats', @@ -170,6 +171,7 @@ def hardware_thread(end_event, hw_queue) -> None: onroad_conditions: dict[str, bool] = { "ignition": False, + "not_onroad_cycle": True, } startup_conditions: dict[str, bool] = {} startup_conditions_prev: dict[str, bool] = {} @@ -195,6 +197,7 @@ def hardware_thread(end_event, hw_queue) -> None: should_start_prev = False in_car = False engaged_prev = False + offroad_cycle_count = 0 params = Params() power_monitor = PowerMonitoring() @@ -211,6 +214,12 @@ def hardware_thread(end_event, hw_queue) -> None: peripheralState = sm['peripheralState'] peripheral_panda_present = peripheralState.pandaType != log.PandaState.PandaType.unknown + # handle requests to cycle system started state + if params.get_bool("OnroadCycleRequested"): + params.put_bool("OnroadCycleRequested", False) + offroad_cycle_count = sm.frame + onroad_conditions["not_onroad_cycle"] = (sm.frame - offroad_cycle_count) >= ONROAD_CYCLE_TIME * SERVICE_LIST['pandaStates'].frequency + if sm.updated['pandaStates'] and len(pandaStates) > 0: # Set ignition based on any panda connected @@ -231,7 +240,7 @@ def hardware_thread(end_event, hw_queue) -> None: cloudlog.error("panda timed out onroad") # Run at 2Hz, plus either edge of ignition - ign_edge = (started_ts is not None) != onroad_conditions["ignition"] + ign_edge = (started_ts is not None) != all(onroad_conditions.values()) if (sm.frame % round(SERVICE_LIST['pandaStates'].frequency * DT_HW) != 0) and not ign_edge: continue From 3d53df644ea7bd3c1fedc3610792eb640a161ffc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 27 May 2025 20:27:54 -0700 Subject: [PATCH 350/477] Live calibration reset button (#35364) * allow reset button too * better lang * copy * fix * meh we don't do this anywhere else --- selfdrive/ui/qt/offroad/settings.cc | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index baac582645..e81b58b919 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -89,7 +89,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { toggle->setEnabled(!locked); if (needs_restart && !locked) { - toggle->setDescription(toggle->getDescription() + " " + tr("Changing this setting will restart openpilot if the car is powered on.")); + toggle->setDescription(toggle->getDescription() + tr(" Changing this setting will restart openpilot if the car is powered on.")); QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) { toggle->setEnabled(!engaged); @@ -210,12 +210,20 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), ""); connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription); connect(resetCalibBtn, &ButtonControl::clicked, [&]() { - if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) { - params.remove("CalibrationParams"); - params.remove("LiveTorqueParameters"); - params.remove("LiveParameters"); - params.remove("LiveParametersV2"); - params.remove("LiveDelay"); + if (!uiState()->engaged()) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), tr("Reset"), this)) { + // Check engaged again in case it changed while the dialog was open + if (!uiState()->engaged()) { + params.remove("CalibrationParams"); + params.remove("LiveTorqueParameters"); + params.remove("LiveParameters"); + params.remove("LiveParametersV2"); + params.remove("LiveDelay"); + params.putBool("OnroadCycleRequested", true); + } + } + } else { + ConfirmationDialog::alert(tr("Disengage to Reset Calibration"), this); } }); addItem(resetCalibBtn); @@ -255,7 +263,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) { }); QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { for (auto btn : findChildren()) { - if (btn != pair_device) { + if (btn != pair_device && btn != resetCalibBtn) { btn->setEnabled(offroad); } } @@ -309,6 +317,7 @@ void DevicePanel::updateCalibDescription() { qInfo() << "invalid CalibrationParams"; } } + desc += tr(" Resetting calibration will restart openpilot if the car is powered on."); qobject_cast(sender())->setDescription(desc); } From feaef58188fc38a7b850541197548c22419de058 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 27 May 2025 21:20:30 -0700 Subject: [PATCH 351/477] AGNOS 12.3 (#35362) 12.3 --- launch_env.sh | 2 +- system/hardware/tici/agnos.json | 20 +++++------ system/hardware/tici/all-partitions.json | 44 ++++++++++++------------ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/launch_env.sh b/launch_env.sh index 6f31fcf776..29e3e38905 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1 export VECLIB_MAXIMUM_THREADS=1 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.2" + export AGNOS_VERSION="12.3" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index e209613f42..7e5b9f187d 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -56,28 +56,28 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968.img.xz", - "hash": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", - "hash_raw": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", + "url": "https://commadist.azureedge.net/agnosupdate/boot-4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42.img.xz", + "hash": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42", + "hash_raw": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "800868bd9d340f1fdf8340924caca374409624658324607621663fc5c7d10d4f" + "ondevice_hash": "6b7b3371100ad36d8a5a9ff19a1663b9b9e2d5e99cbe3cf9255e9c3017291ce3" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img.xz", - "hash": "226794914e9d157b34cc86f1fbe1f2827a9a9919dbe560021b61573a7e5d3101", - "hash_raw": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", + "url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img.xz", + "hash": "993d6a1cd2b684e2b1cf6ff840f8996f02a529011372d9c1471e4c80719e7da9", + "hash_raw": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "236890f04ee21b7eb92a59bc47971bd96cad5a4381ed8935eb4be0cb5a4cf48b", + "ondevice_hash": "59db25651da977eeb16a1af741fd01fc3d6b50d21544b1a7428b7c86b2cdef2d", "alt": { - "hash": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", - "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img", + "hash": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa", + "url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img", "size": 5368709120 } } diff --git a/system/hardware/tici/all-partitions.json b/system/hardware/tici/all-partitions.json index 7e39d275cc..bc1141f763 100644 --- a/system/hardware/tici/all-partitions.json +++ b/system/hardware/tici/all-partitions.json @@ -339,62 +339,62 @@ }, { "name": "boot", - "url": "https://commadist.azureedge.net/agnosupdate/boot-eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968.img.xz", - "hash": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", - "hash_raw": "eb0dd0a8ffdc1c03aa28d055788d70be885a00f523b552d7acf79b954838e968", + "url": "https://commadist.azureedge.net/agnosupdate/boot-4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42.img.xz", + "hash": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42", + "hash_raw": "4143170bad94968fd9be870b1498b4100bf273ed0aec2a2601c9017991d4bd42", "size": 18479104, "sparse": false, "full_check": true, "has_ab": true, - "ondevice_hash": "800868bd9d340f1fdf8340924caca374409624658324607621663fc5c7d10d4f" + "ondevice_hash": "6b7b3371100ad36d8a5a9ff19a1663b9b9e2d5e99cbe3cf9255e9c3017291ce3" }, { "name": "system", - "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img.xz", - "hash": "226794914e9d157b34cc86f1fbe1f2827a9a9919dbe560021b61573a7e5d3101", - "hash_raw": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", + "url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img.xz", + "hash": "993d6a1cd2b684e2b1cf6ff840f8996f02a529011372d9c1471e4c80719e7da9", + "hash_raw": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa", "size": 5368709120, "sparse": true, "full_check": false, "has_ab": true, - "ondevice_hash": "236890f04ee21b7eb92a59bc47971bd96cad5a4381ed8935eb4be0cb5a4cf48b", + "ondevice_hash": "59db25651da977eeb16a1af741fd01fc3d6b50d21544b1a7428b7c86b2cdef2d", "alt": { - "hash": "4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1", - "url": "https://commadist.azureedge.net/agnosupdate/system-4c3fa88833a8a3876653fa53a658769ecc64be8fc6f6a28ce520ef79bf3061c1.img", + "hash": "c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa", + "url": "https://commadist.azureedge.net/agnosupdate/system-c51bb5841011728f7cf108a9138ba68228ffb4232dfd91d6e082a6d8a6a8deaa.img", "size": 5368709120 } }, { "name": "userdata_90", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-f6d24876234f6bea9cc753892eea99ac4b0c8646e93b93d76fc5dddce67347f1.img.xz", - "hash": "2485459e9fbfc0d88a59a017bfa193d97b80afed30d6f59db711e53f62a21c72", - "hash_raw": "f6d24876234f6bea9cc753892eea99ac4b0c8646e93b93d76fc5dddce67347f1", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_90-89a161f17b86637413fe10a641550110b626b699382f5138c02267b7866a8494.img.xz", + "hash": "99d9e6cf6755581c6879bbf442bd62212beb8a04116e965ab987135b8842188b", + "hash_raw": "89a161f17b86637413fe10a641550110b626b699382f5138c02267b7866a8494", "size": 96636764160, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "893cfb3e95d6025b9fa6a5c8c3b97bf2abc4ff036c3da846a07536653dbb3a1d" + "ondevice_hash": "24ea29ab9c4ecec0568a4aa83e38790fedfce694060e90f4bde725931386ff41" }, { "name": "userdata_89", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-14f0f02e53ce62a79b337d7b3174dcaffebaa4130264bb5ed9105d942338230b.img.xz", - "hash": "9222aebb280531b7015ee9bab3848dd195567d075b6f37f74d4cd0186685a11f", - "hash_raw": "14f0f02e53ce62a79b337d7b3174dcaffebaa4130264bb5ed9105d942338230b", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_89-cdd3401168819987c4840765bba1aa2217641b1a6a4165c412f44cac14ccfcbf.img.xz", + "hash": "5fbfa008a7f6b58ab01d4d171f3185924d4c9db69b54f4bfc0f214c6f17c2435", + "hash_raw": "cdd3401168819987c4840765bba1aa2217641b1a6a4165c412f44cac14ccfcbf", "size": 95563022336, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "a5459ec858d39e3d0709a09af430cb644fe640f92e0cd216b138f01401e4e17e" + "ondevice_hash": "c07dc2e883a23d4a24d976cdf53a767a2fd699c8eeb476d60cdf18e84b417a52" }, { "name": "userdata_30", - "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-180ad331538f20c00218d41591afed3a25bb0f76fa30b1a665cad102cf6c9f7d.img.xz", - "hash": "7fc09317f005676150bfcced43ebb1aa919d854c2511d547a588a35c1122bfe3", - "hash_raw": "180ad331538f20c00218d41591afed3a25bb0f76fa30b1a665cad102cf6c9f7d", + "url": "https://commadist.azureedge.net/agnosupdate/userdata_30-2a8e8278b3bb545e6d7292c2417ccebdca9b47507eb5924f7c1e068737a7edfd.img.xz", + "hash": "b3bc293c9c5e0480ef663e980c8ccb2fb83ffd230c85f8797830fb61b8f59360", + "hash_raw": "2a8e8278b3bb545e6d7292c2417ccebdca9b47507eb5924f7c1e068737a7edfd", "size": 32212254720, "sparse": true, "full_check": true, "has_ab": false, - "ondevice_hash": "1a2f2d14c922bd2895073446460a0bd680711a7b14318a4402631635e6d133b3" + "ondevice_hash": "8dae1cda089828c750d1d646337774ccd9432f567ecefde19a06dc7feeda9cd3" } ] \ No newline at end of file From 6c28575573b263b50469de05abf4e078dd5d06a5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 28 May 2025 12:47:55 +0800 Subject: [PATCH 352/477] system/ui: GPU-accelerated polygon rendering with anti-aliasing and gradients (#35357) * Add GPU-accelerated polygon rendering with anti-aliased edges and gradient support * use np array * update ModelRenderer * ndarray * cleanup * improve shader * Revert "improve shader" This reverts commit 992247617a9947bceb365f7b056fed6ebed3793d. * improve shader for smoother edges --- system/ui/lib/shader_polygon.py | 338 +++++++++++++++++++++++++++++ system/ui/onroad/model_renderer.py | 101 +++++---- 2 files changed, 397 insertions(+), 42 deletions(-) create mode 100644 system/ui/lib/shader_polygon.py diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py new file mode 100644 index 0000000000..dfbfa969d5 --- /dev/null +++ b/system/ui/lib/shader_polygon.py @@ -0,0 +1,338 @@ +import pyray as rl +import numpy as np +from typing import Any + + +FRAGMENT_SHADER = """ +#version 300 es +precision mediump float; + +in vec2 fragTexCoord; +out vec4 finalColor; + +uniform vec2 points[100]; +uniform int pointCount; +uniform vec4 fillColor; +uniform vec2 resolution; + +uniform bool useGradient; +uniform vec2 gradientStart; +uniform vec2 gradientEnd; +uniform vec4 gradientColors[8]; +uniform float gradientStops[8]; +uniform int gradientColorCount; + +vec4 getGradientColor(vec2 pos) { + vec2 gradientDir = gradientEnd - gradientStart; + float gradientLength = length(gradientDir); + + if (gradientLength < 0.001) return gradientColors[0]; + + vec2 normalizedDir = gradientDir / gradientLength; + vec2 pointVec = pos - gradientStart; + float projection = dot(pointVec, normalizedDir); + float t = clamp(projection / gradientLength, 0.0, 1.0); + + for (int i = 0; i < gradientColorCount - 1; i++) { + if (t >= gradientStops[i] && t <= gradientStops[i+1]) { + float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]); + return mix(gradientColors[i], gradientColors[i+1], segmentT); + } + } + + return gradientColors[gradientColorCount-1]; +} + +bool isPointInsidePolygon(vec2 p) { + if (pointCount < 3) return false; + + if (pointCount == 3) { + vec2 v0 = points[0]; + vec2 v1 = points[1]; + vec2 v2 = points[2]; + + float d = (v1.y - v2.y) * (v0.x - v2.x) + (v2.x - v1.x) * (v0.y - v2.y); + if (abs(d) < 0.0001) return false; + + float a = ((v1.y - v2.y) * (p.x - v2.x) + (v2.x - v1.x) * (p.y - v2.y)) / d; + float b = ((v2.y - v0.y) * (p.x - v2.x) + (v0.x - v2.x) * (p.y - v2.y)) / d; + float c = 1.0 - a - b; + + return (a >= 0.0 && b >= 0.0 && c >= 0.0); + } + + bool inside = false; + for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) { + if (distance(points[i], points[j]) < 0.0001) continue; + + float dy = points[j].y - points[i].y; + if (abs(dy) < 0.0001) continue; + + if (((points[i].y > p.y) != (points[j].y > p.y))) { + float x_intersect = points[i].x + (points[j].x - points[i].x) * (p.y - points[i].y) / dy; + if (p.x < x_intersect) { + inside = !inside; + } + } + } + return inside; +} + +float distanceToEdge(vec2 p) { + float minDist = 1000.0; + + for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) { + vec2 edge0 = points[j]; + vec2 edge1 = points[i]; + + if (distance(edge0, edge1) < 0.0001) continue; + + vec2 v1 = p - edge0; + vec2 v2 = edge1 - edge0; + float l2 = dot(v2, v2); + + if (l2 < 0.0001) { + float dist = length(v1); + minDist = min(minDist, dist); + continue; + } + + float t = clamp(dot(v1, v2) / l2, 0.0, 1.0); + vec2 projection = edge0 + t * v2; + float dist = length(p - projection); + minDist = min(minDist, dist); + } + + return minDist; +} + +float signedDistanceToPolygon(vec2 p) { + float dist = distanceToEdge(p); + bool inside = isPointInsidePolygon(p); + return inside ? dist : -dist; +} + +void main() { + vec2 pixel = fragTexCoord * resolution; + + float signedDist = signedDistanceToPolygon(pixel); + + vec2 pixelGrad = vec2(dFdx(pixel.x), dFdy(pixel.y)); + float pixelSize = length(pixelGrad); + float aaWidth = max(0.5, pixelSize * 1.0); + + float alpha = smoothstep(-aaWidth, aaWidth, signedDist); + + if (alpha > 0.0) { + vec4 color; + if (useGradient) { + color = getGradientColor(fragTexCoord); + } else { + color = fillColor; + } + finalColor = vec4(color.rgb, color.a * alpha); + } else { + finalColor = vec4(0.0, 0.0, 0.0, 0.0); + } +} +""" + +# Default vertex shader +VERTEX_SHADER = """ +#version 300 es +in vec3 vertexPosition; +in vec2 vertexTexCoord; +out vec2 fragTexCoord; +uniform mat4 mvp; + +void main() { + fragTexCoord = vertexTexCoord; + gl_Position = mvp * vec4(vertexPosition, 1.0); +} +""" + + +class ShaderState: + _instance: Any = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def __init__(self): + if ShaderState._instance is not None: + raise Exception("This class is a singleton. Use get_instance() instead.") + + self.initialized = False + self.shader = None + self.white_texture = None + + # Shader uniform locations + self.locations = { + 'pointCount': None, + 'fillColor': None, + 'resolution': None, + 'points': None, + 'useGradient': None, + 'gradientStart': None, + 'gradientEnd': None, + 'gradientColors': None, + 'gradientStops': None, + 'gradientColorCount': None, + 'mvp': None, + } + + def initialize(self): + if self.initialized: + return + + vertex_shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAGMENT_SHADER) + self.shader = vertex_shader + + # Create and cache white texture + white_img = rl.gen_image_color(2, 2, rl.WHITE) + self.white_texture = rl.load_texture_from_image(white_img) + rl.set_texture_filter(self.white_texture, rl.TEXTURE_FILTER_BILINEAR) + rl.unload_image(white_img) + + # Cache all uniform locations + for uniform in self.locations.keys(): + self.locations[uniform] = rl.get_shader_location(self.shader, uniform) + + # Setup default MVP matrix + mvp_ptr = rl.ffi.new("float[16]", [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]) + rl.set_shader_value_matrix(self.shader, self.locations['mvp'], rl.Matrix(*mvp_ptr)) + + self.initialized = True + + def cleanup(self): + if not self.initialized: + return + + if self.white_texture: + rl.unload_texture(self.white_texture) + self.white_texture = None + + if self.shader: + rl.unload_shader(self.shader) + self.shader = None + + self.initialized = False + + +def draw_polygon(points: np.ndarray, color=None, gradient=None): + """ + Draw a complex polygon using shader-based even-odd fill rule + + Args: + points: List of (x,y) points defining the polygon + color: Solid fill color (rl.Color) + gradient: Dict with gradient parameters: + { + 'start': (x1, y1), # Start point (normalized 0-1) + 'end': (x2, y2), # End point (normalized 0-1) + 'colors': [rl.Color], # List of colors at stops + 'stops': [float] # List of positions (0-1) + } + """ + if len(points) < 3: + return + + # Get shader state singleton + state = ShaderState.get_instance() + + # Initialize shader if not already done + if not state.initialized: + state.initialize() + + # Find bounding box + min_xy = np.min(points, axis=0) + min_x, min_y = min_xy + max_x, max_y = np.max(points, axis=0) + + width = max(1, max_x - min_x) + height = max(1, max_y - min_y) + + # Transform points to shader space + transformed_points = points - min_xy + + # Set basic shader uniforms using cached locations + point_count_ptr = rl.ffi.new("int[]", [len(transformed_points)]) + rl.set_shader_value(state.shader, state.locations['pointCount'], point_count_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + + resolution_ptr = rl.ffi.new("float[]", [width, height]) + rl.set_shader_value(state.shader, state.locations['resolution'], resolution_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) + + # Set points + flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32)) + points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data) + rl.set_shader_value_v( + state.shader, state.locations['points'], points_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2, len(transformed_points) + ) + + # Set gradient or solid color based on what was provided + if gradient: + # Enable gradient + use_gradient_ptr = rl.ffi.new("int[]", [1]) + rl.set_shader_value(state.shader, state.locations['useGradient'], use_gradient_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + + # Set gradient start/end + start_ptr = rl.ffi.new("float[]", [gradient['start'][0], gradient['start'][1]]) + end_ptr = rl.ffi.new("float[]", [gradient['end'][0], gradient['end'][1]]) + rl.set_shader_value(state.shader, state.locations['gradientStart'], start_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) + rl.set_shader_value(state.shader, state.locations['gradientEnd'], end_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) + + # Set gradient colors + colors = gradient['colors'] + color_count = min(len(colors), 8) # Max 8 colors + colors_ptr = rl.ffi.new("float[]", color_count * 4) + for i, c in enumerate(colors[:color_count]): + colors_ptr[i * 4] = c.r / 255.0 + colors_ptr[i * 4 + 1] = c.g / 255.0 + colors_ptr[i * 4 + 2] = c.b / 255.0 + colors_ptr[i * 4 + 3] = c.a / 255.0 + rl.set_shader_value_v( + state.shader, state.locations['gradientColors'], colors_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4, color_count + ) + + # Set gradient stops + stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)]) + stops_ptr = rl.ffi.new("float[]", color_count) + for i, s in enumerate(stops[:color_count]): + stops_ptr[i] = s + rl.set_shader_value_v( + state.shader, state.locations['gradientStops'], stops_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_FLOAT, color_count + ) + + # Set color count + color_count_ptr = rl.ffi.new("int[]", [color_count]) + rl.set_shader_value(state.shader, state.locations['gradientColorCount'], color_count_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + else: + # Disable gradient + use_gradient_ptr = rl.ffi.new("int[]", [0]) + rl.set_shader_value(state.shader, state.locations['useGradient'], use_gradient_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + + # Set solid color + if color is None: + color = rl.WHITE + fill_color_ptr = rl.ffi.new("float[]", [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0]) + rl.set_shader_value(state.shader, state.locations['fillColor'], fill_color_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4) + + # Draw with shader + rl.begin_shader_mode(state.shader) + rl.draw_texture_pro( + state.white_texture, + rl.Rectangle(0, 0, 2, 2), + rl.Rectangle(int(min_x), int(min_y), int(width), int(height)), + rl.Vector2(0, 0), + 0.0, + rl.WHITE, + ) + rl.end_shader_mode() + + +def cleanup_shader_resources(): + state = ShaderState.get_instance() + state.cleanup() diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index d0b66666fa..9060dd3f74 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -4,6 +4,7 @@ import numpy as np import pyray as rl from cereal import messaging, car from openpilot.common.params import Params +from openpilot.system.ui.lib.shader_polygon import draw_polygon CLIP_MARGIN = 500 @@ -30,14 +31,14 @@ class ModelRenderer: self._experimental_mode = False self._blend_factor = 1.0 self._prev_allow_throttle = True - self._lane_line_probs = [0.0] * 4 - self._road_edge_stds = [0.0] * 2 + self._lane_line_probs = np.zeros(4, dtype=np.float32) + self._road_edge_stds = np.zeros(2, dtype=np.float32) self._path_offset_z = 1.22 # Initialize empty polygon vertices - self._track_vertices = [] - self._lane_line_vertices = [[] for _ in range(4)] - self._road_edge_vertices = [[] for _ in range(2)] + self._track_vertices = np.empty((0, 2), dtype=np.float32) + self._lane_line_vertices = [np.empty((0, 2), dtype=np.float32) for _ in range(4)] + self._road_edge_vertices = [np.empty((0, 2), dtype=np.float32) for _ in range(2)] self._lead_vertices = [None, None] # Transform matrix (3x3 for car space to screen space) @@ -145,29 +146,29 @@ class ModelRenderer: def _draw_lane_lines(self): """Draw lane lines and road edges""" - for i in range(4): + for i, vertices in enumerate(self._lane_line_vertices): # Skip if no vertices - if not self._lane_line_vertices[i]: + if vertices.size == 0: continue # Draw lane line alpha = np.clip(self._lane_line_probs[i], 0.0, 0.7) color = rl.Color(255, 255, 255, int(alpha * 255)) - self._draw_polygon(self._lane_line_vertices[i], color) + draw_polygon(vertices, color) - for i in range(2): + for i, vertices in enumerate(self._road_edge_vertices): # Skip if no vertices - if not self._road_edge_vertices[i]: + if vertices.size == 0: continue # Draw road edge alpha = np.clip(1.0 - self._road_edge_stds[i], 0.0, 1.0) color = rl.Color(255, 0, 0, int(alpha * 255)) - self._draw_polygon(self._road_edge_vertices[i], color) + draw_polygon(vertices, color) def _draw_path(self, sm, model, height): """Draw the path polygon with gradient based on acceleration""" - if not self._track_vertices: + if self._track_vertices.size == 0: return if self._experimental_mode: @@ -175,16 +176,29 @@ class ModelRenderer: acceleration = model.acceleration.x max_len = min(len(self._track_vertices) // 2, len(acceleration)) - # Create gradient colors for path sections - for i in range(max_len): + # Find midpoint index for polygon + mid_point = len(self._track_vertices) // 2 + + # For acceleration-based coloring, process segments separately + left_side = self._track_vertices[:mid_point] + right_side = self._track_vertices[mid_point:][::-1] # Reverse for proper winding + + # Create segments for gradient coloring + segment_colors = [] + gradient_stops = [] + + for i in range(max_len - 1): + if i >= len(left_side) - 1 or i >= len(right_side) - 1: + break + track_idx = max_len - i - 1 # flip idx to start from bottom right - track_y = self._track_vertices[track_idx][1] + # Skip points out of frame - if track_y < 0 or track_y > height: + if left_side[track_idx][1] < 0 or left_side[track_idx][1] > height: continue # Calculate color based on acceleration - lin_grad_point = (height - track_y) / height + lin_grad_point = (height - left_side[track_idx][1]) / height # speed up: 120, slow down: 0 path_hue = max(min(60 + acceleration[i] * 35, 120), 0) @@ -197,12 +211,22 @@ class ModelRenderer: # Use HSL to RGB conversion color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha) - # TODO: This is simplified - a full implementation would create a gradient fill - segment = self._track_vertices[track_idx : track_idx + 2] + self._track_vertices[-track_idx - 2 : -track_idx] - self._draw_polygon(segment, color) + # Create quad segment + gradient_stops.append(lin_grad_point) + segment_colors.append(color) - # Skip a point, unless next is last - i += 1 if i + 2 < max_len else 0 + if len(segment_colors) < 2: + draw_polygon(self._track_vertices, rl.Color(255, 255, 255, 30)) + return + + # Create gradient specification + gradient = { + 'start': (0.0, 1.0), # Bottom of path + 'end': (0.0, 0.0), # Top of path + 'colors': segment_colors, + 'stops': gradient_stops, + } + draw_polygon(self._track_vertices, gradient=gradient) else: # Draw with throttle/no throttle gradient allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control @@ -226,7 +250,13 @@ class ModelRenderer: self._blend_colors(begin_colors[2], end_colors[2], self._blend_factor), ] - self._draw_polygon(self._track_vertices, colors[0]) + gradient = { + 'start': (0.0, 1.0), # Bottom of path + 'end': (0.0, 0.0), # Top of path + 'colors': colors, + 'stops': [0.0, 1.0], + } + draw_polygon(self._track_vertices, gradient=gradient) def _draw_lead(self, lead_data, vd, rect): """Draw lead vehicle indicator""" @@ -284,14 +314,14 @@ class ModelRenderer: return (x, y) - def _map_line_to_polygon(self, line, y_off, z_off, max_idx, allow_invert=True): + def _map_line_to_polygon(self, line, y_off, z_off, max_idx, allow_invert=True)-> np.ndarray: """Convert a 3D line to a 2D polygon for drawing""" line_x = line.x line_y = line.y line_z = line.z - left_points = [] - right_points = [] + left_points: list[tuple[float, float]] = [] + right_points: list[tuple[float, float]] = [] for i in range(max_idx + 1): # Skip points with negative x (behind camera) @@ -309,23 +339,10 @@ class ModelRenderer: left_points.append(left) right_points.append(right) - if not left_points: - return [] + if not left_points or not right_points: + return np.empty((0, 2), dtype=np.float32) - return left_points + right_points[::-1] - - def _draw_polygon(self, points, color): - # TODO: Enhance polygon drawing to support even-odd fill rule efficiently, as Raylib lacks native support. - # Use a faster triangulation algorithm (e.g., ear clipping) or GPU shader for - # efficient rendering of lane lines, road edges, and path polygons. - if len(points) <= 8: - rl.draw_triangle_fan(points, len(points), color) - else: - for i in range(1, len(points) - 1): - rl.draw_triangle(points[0], points[i], points[i + 1], color) - - for i in range(len(points)): - rl.draw_line_ex(points[i], points[(i + 1) % len(points)], 1.5, color) + return np.array(left_points + right_points[::-1], dtype=np.float32) @staticmethod def _map_val(x, x0, x1, y0, y1): From 21b86c4f2b503c7f40833f048c62844a51ebfb34 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Wed, 28 May 2025 01:26:21 -0400 Subject: [PATCH 353/477] Update Python packages (#949) --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index c5b96b4b4c..83d7cf4fd1 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit c5b96b4b4c357b909330426a6c516afa16b1a08c +Subproject commit 83d7cf4fd128cc2dabb945d043e838ab404b16b6 From 9460ff8f305045d7cf0aa8b40718b47824e633da Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 May 2025 00:26:38 +0800 Subject: [PATCH 354/477] system/ui: fix gradient colors and path stops in path rendering (#35367) Correct gradient colors and direction in path rendering --- system/ui/onroad/model_renderer.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index 9060dd3f74..649360a7e3 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -13,15 +13,15 @@ MAX_DRAW_DISTANCE = 100.0 THROTTLE_COLORS = [ - rl.Color(0, 231, 130, 102), # Green with alpha 0.4 - rl.Color(112, 247, 35, 89), # Lime with alpha 0.35 - rl.Color(112, 247, 35, 0), # Transparent lime + rl.Color(25, 235, 99, 102), # HSLF(148/360, 0.94, 0.51, 0.4) + rl.Color(92, 255, 32, 89), # HSLF(112/360, 1.0, 0.68, 0.35) + rl.Color(92, 255, 32, 0), # HSLF(112/360, 1.0, 0.68, 0.0) ] NO_THROTTLE_COLORS = [ - rl.Color(242, 242, 242, 102), # Light gray with alpha 0.4 - rl.Color(242, 242, 242, 89), # Light gray with alpha 0.35 - rl.Color(242, 242, 242, 0), # Transparent light gray + rl.Color(242, 242, 242, 102), # HSLF(148/360, 0.0, 0.95, 0.4) + rl.Color(242, 242, 242, 89), # HSLF(112/360, 0.0, 0.95, 0.35) + rl.Color(242, 242, 242, 0), # HSLF(112/360, 0.0, 0.95, 0.0) ] @@ -254,7 +254,7 @@ class ModelRenderer: 'start': (0.0, 1.0), # Bottom of path 'end': (0.0, 0.0), # Top of path 'colors': colors, - 'stops': [0.0, 1.0], + 'stops': [0.0, 0.5, 1.0], } draw_polygon(self._track_vertices, gradient=gradient) @@ -364,14 +364,14 @@ class ModelRenderer: return rl.Color(r_val, g_val, b_val, a_val) @staticmethod - def _blend_colors(start, end, t): + def _blend_colors(start: rl.Color, end: rl.Color, t: float) -> rl.Color: """Blend between two colors with factor t""" if t >= 1.0: return end return rl.Color( - int((1 - t) * start.r + t * end.r), - int((1 - t) * start.g + t * end.g), - int((1 - t) * start.b + t * end.b), - int((1 - t) * start.a + t * end.a), + int((1.0 - t) * start.r + t * end.r), + int((1.0 - t) * start.g + t * end.g), + int((1.0 - t) * start.b + t * end.b), + int((1.0 - t) * start.a + t * end.a), ) From 3682fac7b6f43b1417236d4e7d69035feb6e8e31 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 May 2025 00:29:33 +0800 Subject: [PATCH 355/477] system/ui: optimizes the draw_polygon() and improving code maintainability (#35366) improve draw_polygon --- system/ui/lib/shader_polygon.py | 132 ++++++++++++++++---------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index dfbfa969d5..23f54efcd7 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -152,8 +152,14 @@ void main() { """ +UNIFORM_INT = rl.ShaderUniformDataType.SHADER_UNIFORM_INT +UNIFORM_FLOAT = rl.ShaderUniformDataType.SHADER_UNIFORM_FLOAT +UNIFORM_VEC2 = rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2 +UNIFORM_VEC4 = rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4 + + class ShaderState: - _instance: Any = None + _instance: Any = None @classmethod def get_instance(cls): @@ -184,6 +190,19 @@ class ShaderState: 'mvp': None, } + # Pre-allocated FFI objects + self.point_count_ptr = rl.ffi.new("int[]", [0]) + self.resolution_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.fill_color_ptr = rl.ffi.new("float[]", [0.0, 0.0, 0.0, 0.0]) + self.use_gradient_ptr = rl.ffi.new("int[]", [0]) + self.gradient_start_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.gradient_end_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.color_count_ptr = rl.ffi.new("int[]", [0]) + + # Pre-allocate gradient arrays (max 8 colors) + self.gradient_colors_ptr = rl.ffi.new("float[]", 32) # 8 colors * 4 components + self.gradient_stops_ptr = rl.ffi.new("float[]", 8) + def initialize(self): if self.initialized: return @@ -222,12 +241,46 @@ class ShaderState: self.initialized = False +def _configure_shader_color(state, color, gradient): + """Configure shader uniforms for solid color or gradient rendering""" + state.use_gradient_ptr[0] = 1 if gradient else 0 + rl.set_shader_value(state.shader, state.locations['useGradient'], state.use_gradient_ptr, UNIFORM_INT) + + if gradient: + # Set gradient start/end + state.gradient_start_ptr[0:2] = gradient['start'] + state.gradient_end_ptr[0:2] = gradient['end'] + rl.set_shader_value(state.shader, state.locations['gradientStart'], state.gradient_start_ptr, UNIFORM_VEC2) + rl.set_shader_value(state.shader, state.locations['gradientEnd'], state.gradient_end_ptr, UNIFORM_VEC2) + + # Set gradient colors + colors = gradient['colors'] + color_count = min(len(colors), 8) # Max 8 colors + for i, c in enumerate(colors[:color_count]): + base_idx = i * 4 + state.gradient_colors_ptr[base_idx:base_idx+4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0] + rl.set_shader_value_v(state.shader, state.locations['gradientColors'], state.gradient_colors_ptr, UNIFORM_VEC4, color_count) + + # Set gradient stops + stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)]) + state.gradient_stops_ptr[0:color_count] = stops[:color_count] + rl.set_shader_value_v(state.shader, state.locations['gradientStops'], state.gradient_stops_ptr, UNIFORM_FLOAT, color_count) + + # Set color count + state.color_count_ptr[0] = color_count + rl.set_shader_value(state.shader, state.locations['gradientColorCount'], state.color_count_ptr, UNIFORM_INT) + else: + color = color or rl.WHITE # Default to white if no color provided + state.fill_color_ptr[0:4] = [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0] + rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4) + + def draw_polygon(points: np.ndarray, color=None, gradient=None): """ Draw a complex polygon using shader-based even-odd fill rule Args: - points: List of (x,y) points defining the polygon + points: numpy array of (x,y) points defining the polygon color: Solid fill color (rl.Color) gradient: Dict with gradient parameters: { @@ -240,92 +293,41 @@ def draw_polygon(points: np.ndarray, color=None, gradient=None): if len(points) < 3: return - # Get shader state singleton state = ShaderState.get_instance() - - # Initialize shader if not already done if not state.initialized: state.initialize() # Find bounding box min_xy = np.min(points, axis=0) - min_x, min_y = min_xy - max_x, max_y = np.max(points, axis=0) + max_xy = np.max(points, axis=0) - width = max(1, max_x - min_x) - height = max(1, max_y - min_y) + width = max(1, max_xy[0] - min_xy[0]) + height = max(1, max_xy[1] - min_xy[1]) # Transform points to shader space transformed_points = points - min_xy - # Set basic shader uniforms using cached locations - point_count_ptr = rl.ffi.new("int[]", [len(transformed_points)]) - rl.set_shader_value(state.shader, state.locations['pointCount'], point_count_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + state.point_count_ptr[0] = len(transformed_points) + rl.set_shader_value(state.shader, state.locations['pointCount'], state.point_count_ptr, UNIFORM_INT) + + state.resolution_ptr[0:2] = [width, height] + rl.set_shader_value(state.shader, state.locations['resolution'], state.resolution_ptr, UNIFORM_VEC2) - resolution_ptr = rl.ffi.new("float[]", [width, height]) - rl.set_shader_value(state.shader, state.locations['resolution'], resolution_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) # Set points flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32)) points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data) - rl.set_shader_value_v( - state.shader, state.locations['points'], points_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2, len(transformed_points) - ) + rl.set_shader_value_v(state.shader, state.locations['points'], points_ptr, UNIFORM_VEC2, len(transformed_points)) - # Set gradient or solid color based on what was provided - if gradient: - # Enable gradient - use_gradient_ptr = rl.ffi.new("int[]", [1]) - rl.set_shader_value(state.shader, state.locations['useGradient'], use_gradient_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) - - # Set gradient start/end - start_ptr = rl.ffi.new("float[]", [gradient['start'][0], gradient['start'][1]]) - end_ptr = rl.ffi.new("float[]", [gradient['end'][0], gradient['end'][1]]) - rl.set_shader_value(state.shader, state.locations['gradientStart'], start_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) - rl.set_shader_value(state.shader, state.locations['gradientEnd'], end_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) - - # Set gradient colors - colors = gradient['colors'] - color_count = min(len(colors), 8) # Max 8 colors - colors_ptr = rl.ffi.new("float[]", color_count * 4) - for i, c in enumerate(colors[:color_count]): - colors_ptr[i * 4] = c.r / 255.0 - colors_ptr[i * 4 + 1] = c.g / 255.0 - colors_ptr[i * 4 + 2] = c.b / 255.0 - colors_ptr[i * 4 + 3] = c.a / 255.0 - rl.set_shader_value_v( - state.shader, state.locations['gradientColors'], colors_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4, color_count - ) - - # Set gradient stops - stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)]) - stops_ptr = rl.ffi.new("float[]", color_count) - for i, s in enumerate(stops[:color_count]): - stops_ptr[i] = s - rl.set_shader_value_v( - state.shader, state.locations['gradientStops'], stops_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_FLOAT, color_count - ) - - # Set color count - color_count_ptr = rl.ffi.new("int[]", [color_count]) - rl.set_shader_value(state.shader, state.locations['gradientColorCount'], color_count_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) - else: - # Disable gradient - use_gradient_ptr = rl.ffi.new("int[]", [0]) - rl.set_shader_value(state.shader, state.locations['useGradient'], use_gradient_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) - - # Set solid color - if color is None: - color = rl.WHITE - fill_color_ptr = rl.ffi.new("float[]", [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0]) - rl.set_shader_value(state.shader, state.locations['fillColor'], fill_color_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4) + # Configure color/gradient uniforms + _configure_shader_color(state, color, gradient) # Draw with shader rl.begin_shader_mode(state.shader) rl.draw_texture_pro( state.white_texture, rl.Rectangle(0, 0, 2, 2), - rl.Rectangle(int(min_x), int(min_y), int(width), int(height)), + rl.Rectangle(int(min_xy[0]), int(min_xy[1]), int(width), int(height)), rl.Vector2(0, 0), 0.0, rl.WHITE, From db8ecf183f7e8d78807cfcfa6aeac8eed3a3d5e0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 May 2025 02:35:04 +0800 Subject: [PATCH 356/477] system/ui: fix rapid path color transition by correcting hardcoded increment (#35368) refactor color blending with configurable transition duration --- system/ui/onroad/model_renderer.py | 35 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index 649360a7e3..8a87f2f388 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -4,13 +4,15 @@ import numpy as np import pyray as rl from cereal import messaging, car from openpilot.common.params import Params +from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.shader_polygon import draw_polygon CLIP_MARGIN = 500 MIN_DRAW_DISTANCE = 10.0 MAX_DRAW_DISTANCE = 100.0 - +PATH_COLOR_TRANSITION_DURATION = 0.5 # Seconds for color transition animation +PATH_BLEND_INCREMENT = 1.0 / (PATH_COLOR_TRANSITION_DURATION * DEFAULT_FPS) THROTTLE_COLORS = [ rl.Color(25, 235, 99, 102), # HSLF(148/360, 0.94, 0.51, 0.4) @@ -238,22 +240,17 @@ class ModelRenderer: # Update blend factor if self._blend_factor < 1.0: - self._blend_factor = min(self._blend_factor + 0.1, 1.0) + self._blend_factor = min(self._blend_factor + PATH_BLEND_INCREMENT, 1.0) begin_colors = NO_THROTTLE_COLORS if allow_throttle else THROTTLE_COLORS end_colors = THROTTLE_COLORS if allow_throttle else NO_THROTTLE_COLORS # Blend colors based on transition - colors = [ - self._blend_colors(begin_colors[0], end_colors[0], self._blend_factor), - self._blend_colors(begin_colors[1], end_colors[1], self._blend_factor), - self._blend_colors(begin_colors[2], end_colors[2], self._blend_factor), - ] - + blended_colors = self._blend_colors(begin_colors, end_colors, self._blend_factor) gradient = { 'start': (0.0, 1.0), # Bottom of path 'end': (0.0, 0.0), # Top of path - 'colors': colors, + 'colors': blended_colors, 'stops': [0.0, 0.5, 1.0], } draw_polygon(self._track_vertices, gradient=gradient) @@ -364,14 +361,16 @@ class ModelRenderer: return rl.Color(r_val, g_val, b_val, a_val) @staticmethod - def _blend_colors(start: rl.Color, end: rl.Color, t: float) -> rl.Color: - """Blend between two colors with factor t""" + def _blend_colors(begin_colors, end_colors, t): if t >= 1.0: - return end + return end_colors + if t <= 0.0: + return begin_colors - return rl.Color( - int((1.0 - t) * start.r + t * end.r), - int((1.0 - t) * start.g + t * end.g), - int((1.0 - t) * start.b + t * end.b), - int((1.0 - t) * start.a + t * end.a), - ) + inv_t = 1.0 - t + return [rl.Color( + int(inv_t * start.r + t * end.r), + int(inv_t * start.g + t * end.g), + int(inv_t * start.b + t * end.b), + int(inv_t * start.a + t * end.a) + ) for start, end in zip(begin_colors, end_colors, strict=True)] From 26c61d8674800e71ef8f78227a13e6eadfe9a34f Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 May 2025 04:07:19 +0800 Subject: [PATCH 357/477] system/ui: fix incorrect THROTTLE_COLOR (#35370) fix throttle colors --- system/ui/onroad/model_renderer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index 8a87f2f388..ac3ac74430 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -15,9 +15,9 @@ PATH_COLOR_TRANSITION_DURATION = 0.5 # Seconds for color transition animation PATH_BLEND_INCREMENT = 1.0 / (PATH_COLOR_TRANSITION_DURATION * DEFAULT_FPS) THROTTLE_COLORS = [ - rl.Color(25, 235, 99, 102), # HSLF(148/360, 0.94, 0.51, 0.4) - rl.Color(92, 255, 32, 89), # HSLF(112/360, 1.0, 0.68, 0.35) - rl.Color(92, 255, 32, 0), # HSLF(112/360, 1.0, 0.68, 0.0) + rl.Color(13, 248, 122, 102), # HSLF(148/360, 0.94, 0.51, 0.4) + rl.Color(114, 255, 92, 89), # HSLF(112/360, 1.0, 0.68, 0.35) + rl.Color(114, 255, 92, 0), # HSLF(112/360, 1.0, 0.68, 0.0) ] NO_THROTTLE_COLORS = [ From 5bf81048bbddd472e4bc37187ef5f4ea53ad3431 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 28 May 2025 14:58:48 -0700 Subject: [PATCH 358/477] bump opendbc (#35372) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 3ae4dc13cd..ffe9db9b7c 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 3ae4dc13cd9e628672a01651896021214b7c3c17 +Subproject commit ffe9db9b7cffde1a21475d912b5145cd401d0fd4 From 1db3644a2b3f156c9f5790b6cfe0d6a46c4b6b54 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 28 May 2025 17:41:38 -0700 Subject: [PATCH 359/477] car interfaces: pass in if release build (#35373) * dashcam for release * get car * fixes * more fixes * bump * bump --- opendbc_repo | 2 +- selfdrive/car/card.py | 6 ++++-- selfdrive/car/tests/test_car_interfaces.py | 2 +- selfdrive/car/tests/test_models.py | 2 +- selfdrive/test/process_replay/process_replay.py | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index ffe9db9b7c..d33a033a7f 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit ffe9db9b7cffde1a21475d912b5145cd401d0fd4 +Subproject commit d33a033a7fceaf660a1ae1e6e7d00fb4737f6745 diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 502daaa015..3339096307 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -80,6 +80,8 @@ class Car: self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan']) + is_release = self.params.get_bool("IsReleaseBranch") + if CI is None: # wait for one pandaState and one CAN packet print("Waiting for CAN messages...") @@ -97,7 +99,7 @@ class Car: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, num_pandas, cached_params) + self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params) self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP) self.CP = self.CI.CP @@ -116,7 +118,7 @@ class Car: safety_config.safetyModel = structs.CarParams.SafetyModel.noOutput self.CP.safetyConfigs = [safety_config] - if self.CP.secOcRequired and not self.params.get_bool("IsReleaseBranch"): + if self.CP.secOcRequired and not is_release: # Copy user key if available try: with open("/cache/params/SecOCKey") as f: diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 92c7ff8f1c..5c4729ee9a 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -39,7 +39,7 @@ class TestCarInterfaces: args = get_fuzzy_car_interface_args(data.draw) car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'], - alpha_long=args['alpha_long'], docs=False) + alpha_long=args['alpha_long'], is_release=False, docs=False) car_params = car_params.as_reader() car_interface = CarInterface(car_params) assert car_params diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 0a4784aca8..b918f4c29f 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -152,7 +152,7 @@ class TestCarModelBase(unittest.TestCase): cls.openpilot_enabled = cls.car_safety_mode_frame is not None cls.CarInterface = interfaces[cls.platform] - cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, docs=False) + cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False) assert cls.CP assert cls.CP.carFingerprint == cls.platform diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 8de2e60055..4f8aa9d99d 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -362,7 +362,7 @@ def get_car_params_callback(rc, pm, msgs, fingerprint): with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), cached_params=cached_params).CP + CP = get_car(*can_callbacks, lambda obd: None, Params().get_bool("AlphaLongitudinalEnabled"), False, cached_params=cached_params).CP params.put("CarParams", CP.to_bytes()) From fe713b867f5866b64b5e3484495081c97da864c4 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 28 May 2025 19:02:00 -0700 Subject: [PATCH 360/477] CI: fix nightly build (#35374) * CI: fix nightly build * schedule * simplify * cleanup --- .github/workflows/release.yaml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1dd06f77e9..4065b0ef3e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,8 +8,7 @@ jobs: build_masterci: name: build master-ci env: - TARGET_DIR: /tmp/openpilot - ImageOS: ubuntu20 + ImageOS: ubuntu24 container: image: ghcr.io/commaai/openpilot-base:latest runs-on: ubuntu-latest @@ -23,7 +22,7 @@ jobs: sudo apt-get update sudo apt-get install -y libyaml-dev - name: Wait for green check mark - if: ${{ github.event_name != 'workflow_dispatch' }} + if: ${{ github.event_name == 'schedule' }} uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc with: ref: master @@ -39,16 +38,5 @@ jobs: run: | git config --global --add safe.directory '*' git lfs pull - - name: Build master-ci - run: | - release/build_devel.sh - - name: Run tests - run: | - export PYTHONPATH=$TARGET_DIR - cd $TARGET_DIR - scons -j$(nproc) - pytest -n logical selfdrive/car/tests/test_car_interfaces.py - name: Push master-ci - run: | - unset TARGET_DIR - BRANCH=__nightly release/build_devel.sh + run: BRANCH=__nightly release/build_devel.sh From deebbd3e7767d314d1f66faae63e333c0a11f749 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 28 May 2025 19:15:50 -0700 Subject: [PATCH 361/477] add missing Fords to release notes --- RELEASES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASES.md b/RELEASES.md index acdb709c9b..ab21484a68 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,6 +3,7 @@ Version 0.9.9 (2025-05-23) * New driving model * New training architecture using parts from MLSIM * Steering actuation delay is now learned online +* Ford Escape 2023-24 and Ford Kuga 2024 support thanks to incognitojam! * Hyundai Nexo 2021 support thanks to sunnyhaibin! * Tesla Model 3 and Y support thanks to lukasloetkolben! * Lexus RC 2023 support thanks to nelsonjchen! From 51c437c61ed836a5b2321e28895dae0af52d1b5f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 28 May 2025 19:18:03 -0700 Subject: [PATCH 362/477] split --- RELEASES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index ab21484a68..5d74689a2d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,7 +3,8 @@ Version 0.9.9 (2025-05-23) * New driving model * New training architecture using parts from MLSIM * Steering actuation delay is now learned online -* Ford Escape 2023-24 and Ford Kuga 2024 support thanks to incognitojam! +* Ford Escape 2023-24 support thanks to incognitojam! +* Ford Kuga 2024 support thanks to incognitojam! * Hyundai Nexo 2021 support thanks to sunnyhaibin! * Tesla Model 3 and Y support thanks to lukasloetkolben! * Lexus RC 2023 support thanks to nelsonjchen! From 618645f1d7eaec635514e712bac4582691b38862 Mon Sep 17 00:00:00 2001 From: YassineYousfi Date: Wed, 28 May 2025 20:45:53 -0700 Subject: [PATCH 363/477] Revert "Vegan Filet o Fish model " (#35376) Revert "Vegan Filet o Fish model (#35240)" This reverts commit b4d6b52edd815364b9d3503aff6dd3ce1005fe45. --- selfdrive/modeld/models/driving_policy.onnx | 4 ++-- selfdrive/modeld/models/driving_vision.onnx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/modeld/models/driving_policy.onnx b/selfdrive/modeld/models/driving_policy.onnx index 5da6d85300..3601bbb5da 100644 --- a/selfdrive/modeld/models/driving_policy.onnx +++ b/selfdrive/modeld/models/driving_policy.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abbb1f5a74a6d047ed4b7f376b19451a9443986660f136afd0f8d76fc254a579 -size 15976894 +oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36 +size 15966721 diff --git a/selfdrive/modeld/models/driving_vision.onnx b/selfdrive/modeld/models/driving_vision.onnx index 6c8cf592b0..aee6d8f1bf 100644 --- a/selfdrive/modeld/models/driving_vision.onnx +++ b/selfdrive/modeld/models/driving_vision.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dfffac033d7ae8e68aa5763bd9b5dd99ddc748f6a95523c84fc2523eefdec55 +oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624 size 34882971 From ad0e556236d1886955f4a5523ee296d6d8704fa8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 30 May 2025 02:32:08 +0800 Subject: [PATCH 364/477] system/ui: fix gradient rendering to match Qt linear gradients (#35378) Fix shader_polygon gradient rendering to match Qt linear gradients --- system/ui/lib/shader_polygon.py | 147 +++++++++++++++++------------ system/ui/onroad/model_renderer.py | 12 ++- 2 files changed, 94 insertions(+), 65 deletions(-) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index 23f54efcd7..9ecaa926bc 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -2,6 +2,7 @@ import pyray as rl import numpy as np from typing import Any +MAX_GRADIENT_COLORS = 15 FRAGMENT_SHADER = """ #version 300 es @@ -18,21 +19,33 @@ uniform vec2 resolution; uniform bool useGradient; uniform vec2 gradientStart; uniform vec2 gradientEnd; -uniform vec4 gradientColors[8]; -uniform float gradientStops[8]; +uniform vec4 gradientColors[15]; +uniform float gradientStops[15]; uniform int gradientColorCount; +uniform vec2 visibleGradientRange; vec4 getGradientColor(vec2 pos) { vec2 gradientDir = gradientEnd - gradientStart; float gradientLength = length(gradientDir); - if (gradientLength < 0.001) return gradientColors[0]; vec2 normalizedDir = gradientDir / gradientLength; vec2 pointVec = pos - gradientStart; float projection = dot(pointVec, normalizedDir); - float t = clamp(projection / gradientLength, 0.0, 1.0); + float t = projection / gradientLength; + + // Gradient clipping: remap t to visible range + float visibleStart = visibleGradientRange.x; + float visibleEnd = visibleGradientRange.y; + float visibleRange = visibleEnd - visibleStart; + + // Remap t to visible range + if (visibleRange > 0.001) { + t = visibleStart + t * visibleRange; + } + + t = clamp(t, 0.0, 1.0); for (int i = 0; i < gradientColorCount - 1; i++) { if (t >= gradientStops[i] && t <= gradientStops[i+1]) { float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]); @@ -46,36 +59,21 @@ vec4 getGradientColor(vec2 pos) { bool isPointInsidePolygon(vec2 p) { if (pointCount < 3) return false; - if (pointCount == 3) { - vec2 v0 = points[0]; - vec2 v1 = points[1]; - vec2 v2 = points[2]; - - float d = (v1.y - v2.y) * (v0.x - v2.x) + (v2.x - v1.x) * (v0.y - v2.y); - if (abs(d) < 0.0001) return false; - - float a = ((v1.y - v2.y) * (p.x - v2.x) + (v2.x - v1.x) * (p.y - v2.y)) / d; - float b = ((v2.y - v0.y) * (p.x - v2.x) + (v0.x - v2.x) * (p.y - v2.y)) / d; - float c = 1.0 - a - b; - - return (a >= 0.0 && b >= 0.0 && c >= 0.0); - } - - bool inside = false; + int crossings = 0; for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) { - if (distance(points[i], points[j]) < 0.0001) continue; + vec2 pi = points[i]; + vec2 pj = points[j]; - float dy = points[j].y - points[i].y; - if (abs(dy) < 0.0001) continue; + // Skip degenerate edges + if (distance(pi, pj) < 0.001) continue; - if (((points[i].y > p.y) != (points[j].y > p.y))) { - float x_intersect = points[i].x + (points[j].x - points[i].x) * (p.y - points[i].y) / dy; - if (p.x < x_intersect) { - inside = !inside; - } + // Ray-casting + if (((pi.y > p.y) != (pj.y > p.y)) && + (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y + 0.001) + pi.x)) { + crossings++; } } - return inside; + return (crossings & 1) == 1; } float distanceToEdge(vec2 p) { @@ -119,20 +117,14 @@ void main() { vec2 pixelGrad = vec2(dFdx(pixel.x), dFdy(pixel.y)); float pixelSize = length(pixelGrad); - float aaWidth = max(0.5, pixelSize * 1.0); + float aaWidth = max(0.5, pixelSize * 0.5); // Sharper anti-aliasing float alpha = smoothstep(-aaWidth, aaWidth, signedDist); - if (alpha > 0.0) { - vec4 color; - if (useGradient) { - color = getGradientColor(fragTexCoord); - } else { - color = fillColor; - } + vec4 color = useGradient ? getGradientColor(fragTexCoord) : fillColor; finalColor = vec4(color.rgb, color.a * alpha); } else { - finalColor = vec4(0.0, 0.0, 0.0, 0.0); + finalColor = vec4(0.0); } } """ @@ -188,6 +180,7 @@ class ShaderState: 'gradientStops': None, 'gradientColorCount': None, 'mvp': None, + 'visibleGradientRange': None, } # Pre-allocated FFI objects @@ -198,17 +191,15 @@ class ShaderState: self.gradient_start_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.gradient_end_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.color_count_ptr = rl.ffi.new("int[]", [0]) - - # Pre-allocate gradient arrays (max 8 colors) - self.gradient_colors_ptr = rl.ffi.new("float[]", 32) # 8 colors * 4 components - self.gradient_stops_ptr = rl.ffi.new("float[]", 8) + self.visible_gradient_range_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.gradient_colors_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS * 4) + self.gradient_stops_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS) def initialize(self): if self.initialized: return - vertex_shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAGMENT_SHADER) - self.shader = vertex_shader + self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAGMENT_SHADER) # Create and cache white texture white_img = rl.gen_image_color(2, 2, rl.WHITE) @@ -241,21 +232,46 @@ class ShaderState: self.initialized = False -def _configure_shader_color(state, color, gradient): +def _configure_shader_color(state, color, gradient, rect, min_xy, max_xy): """Configure shader uniforms for solid color or gradient rendering""" - state.use_gradient_ptr[0] = 1 if gradient else 0 + use_gradient = 1 if gradient else 0 + state.use_gradient_ptr[0] = use_gradient rl.set_shader_value(state.shader, state.locations['useGradient'], state.use_gradient_ptr, UNIFORM_INT) - if gradient: + if use_gradient: # Set gradient start/end state.gradient_start_ptr[0:2] = gradient['start'] state.gradient_end_ptr[0:2] = gradient['end'] rl.set_shader_value(state.shader, state.locations['gradientStart'], state.gradient_start_ptr, UNIFORM_VEC2) rl.set_shader_value(state.shader, state.locations['gradientEnd'], state.gradient_end_ptr, UNIFORM_VEC2) + # Calculate visible gradient range + width = max_xy[0] - min_xy[0] + height = max_xy[1] - min_xy[1] + + gradient_dir = (gradient['end'][0] - gradient['start'][0], gradient['end'][1] - gradient['start'][1]) + is_vertical = abs(gradient_dir[1]) > abs(gradient_dir[0]) + + visible_start = 0.0 + visible_end = 1.0 + + if is_vertical and height > 0: + visible_start = (rect.y - min_xy[1]) / height + visible_end = visible_start + rect.height / height + elif width > 0: + visible_start = (rect.x - min_xy[0]) / width + visible_end = visible_start + rect.width / width + + # Clamp visible range + visible_start = max(0.0, min(1.0, visible_start)) + visible_end = max(0.0, min(1.0, visible_end)) + + state.visible_gradient_range_ptr[0:2] = [visible_start, visible_end] + rl.set_shader_value(state.shader, state.locations['visibleGradientRange'], state.visible_gradient_range_ptr, UNIFORM_VEC2) + # Set gradient colors colors = gradient['colors'] - color_count = min(len(colors), 8) # Max 8 colors + color_count = min(len(colors), MAX_GRADIENT_COLORS) for i, c in enumerate(colors[:color_count]): base_idx = i * 4 state.gradient_colors_ptr[base_idx:base_idx+4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0] @@ -275,11 +291,12 @@ def _configure_shader_color(state, color, gradient): rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4) -def draw_polygon(points: np.ndarray, color=None, gradient=None): +def draw_polygon(rect: rl.Rectangle, points: np.ndarray, color=None, gradient=None): """ Draw a complex polygon using shader-based even-odd fill rule Args: + rect: Rectangle defining the drawing area points: numpy array of (x,y) points defining the polygon color: Solid fill color (rl.Color) gradient: Dict with gradient parameters: @@ -301,33 +318,43 @@ def draw_polygon(points: np.ndarray, color=None, gradient=None): min_xy = np.min(points, axis=0) max_xy = np.max(points, axis=0) - width = max(1, max_xy[0] - min_xy[0]) - height = max(1, max_xy[1] - min_xy[1]) + # Clip coordinates to rectangle + clip_x = max(rect.x, min_xy[0]) + clip_y = max(rect.y, min_xy[1]) + clip_right = min(rect.x + rect.width, max_xy[0]) + clip_bottom = min(rect.y + rect.height, max_xy[1]) - # Transform points to shader space - transformed_points = points - min_xy + # Check if polygon is completely off-screen + if clip_x >= clip_right or clip_y >= clip_bottom: + return + clipped_width = clip_right - clip_x + clipped_height = clip_bottom - clip_y + + clip_rect = rl.Rectangle(clip_x, clip_y, clipped_width, clipped_height) + + # Transform points relative to the CLIPPED area + transformed_points = points - np.array([clip_x, clip_y]) + + # Set shader values state.point_count_ptr[0] = len(transformed_points) rl.set_shader_value(state.shader, state.locations['pointCount'], state.point_count_ptr, UNIFORM_INT) - state.resolution_ptr[0:2] = [width, height] + state.resolution_ptr[0:2] = [clipped_width, clipped_height] rl.set_shader_value(state.shader, state.locations['resolution'], state.resolution_ptr, UNIFORM_VEC2) - - # Set points flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32)) points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data) rl.set_shader_value_v(state.shader, state.locations['points'], points_ptr, UNIFORM_VEC2, len(transformed_points)) - # Configure color/gradient uniforms - _configure_shader_color(state, color, gradient) + _configure_shader_color(state, color, gradient, clip_rect, min_xy, max_xy) - # Draw with shader + # Render rl.begin_shader_mode(state.shader) rl.draw_texture_pro( state.white_texture, rl.Rectangle(0, 0, 2, 2), - rl.Rectangle(int(min_xy[0]), int(min_xy[1]), int(width), int(height)), + clip_rect, rl.Vector2(0, 0), 0.0, rl.WHITE, diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index ac3ac74430..1f608a6ddb 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -47,6 +47,7 @@ class ModelRenderer: self._car_space_transform = np.zeros((3, 3)) self._transform_dirty = True self._clip_region = None + self._rect = None # Get longitudinal control setting from car parameters car_params = Params().get("CarParams") @@ -64,6 +65,7 @@ class ModelRenderer: return # Set up clipping region + self._rect = rect self._clip_region = rl.Rectangle( rect.x - CLIP_MARGIN, rect.y - CLIP_MARGIN, rect.width + 2 * CLIP_MARGIN, rect.height + 2 * CLIP_MARGIN ) @@ -156,7 +158,7 @@ class ModelRenderer: # Draw lane line alpha = np.clip(self._lane_line_probs[i], 0.0, 0.7) color = rl.Color(255, 255, 255, int(alpha * 255)) - draw_polygon(vertices, color) + draw_polygon(self._rect, vertices, color) for i, vertices in enumerate(self._road_edge_vertices): # Skip if no vertices @@ -166,7 +168,7 @@ class ModelRenderer: # Draw road edge alpha = np.clip(1.0 - self._road_edge_stds[i], 0.0, 1.0) color = rl.Color(255, 0, 0, int(alpha * 255)) - draw_polygon(vertices, color) + draw_polygon(self._rect, vertices, color) def _draw_path(self, sm, model, height): """Draw the path polygon with gradient based on acceleration""" @@ -218,7 +220,7 @@ class ModelRenderer: segment_colors.append(color) if len(segment_colors) < 2: - draw_polygon(self._track_vertices, rl.Color(255, 255, 255, 30)) + draw_polygon(self._rect, self._track_vertices, rl.Color(255, 255, 255, 30)) return # Create gradient specification @@ -228,7 +230,7 @@ class ModelRenderer: 'colors': segment_colors, 'stops': gradient_stops, } - draw_polygon(self._track_vertices, gradient=gradient) + draw_polygon(self._rect, self._track_vertices, gradient=gradient) else: # Draw with throttle/no throttle gradient allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control @@ -253,7 +255,7 @@ class ModelRenderer: 'colors': blended_colors, 'stops': [0.0, 0.5, 1.0], } - draw_polygon(self._track_vertices, gradient=gradient) + draw_polygon(self._rect, self._track_vertices, gradient=gradient) def _draw_lead(self, lead_data, vd, rect): """Draw lead vehicle indicator""" From 2d6662ae9f0af252dbe5d0ab1395810011150be1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 30 May 2025 04:07:43 +0800 Subject: [PATCH 365/477] system/ui: match experimental path rendering with C++ version (#35380) match draw_path with c++ implementation --- system/ui/onroad/model_renderer.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index 1f608a6ddb..afed67aab2 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -180,29 +180,20 @@ class ModelRenderer: acceleration = model.acceleration.x max_len = min(len(self._track_vertices) // 2, len(acceleration)) - # Find midpoint index for polygon - mid_point = len(self._track_vertices) // 2 - - # For acceleration-based coloring, process segments separately - left_side = self._track_vertices[:mid_point] - right_side = self._track_vertices[mid_point:][::-1] # Reverse for proper winding - # Create segments for gradient coloring segment_colors = [] gradient_stops = [] - for i in range(max_len - 1): - if i >= len(left_side) - 1 or i >= len(right_side) - 1: - break - + i = 0 + while i < max_len: track_idx = max_len - i - 1 # flip idx to start from bottom right - - # Skip points out of frame - if left_side[track_idx][1] < 0 or left_side[track_idx][1] > height: + track_y = self._track_vertices[track_idx][1] + if track_y < 0 or track_y > height: + i += 1 continue # Calculate color based on acceleration - lin_grad_point = (height - left_side[track_idx][1]) / height + lin_grad_point = (height - track_y) / height # speed up: 120, slow down: 0 path_hue = max(min(60 + acceleration[i] * 35, 120), 0) @@ -219,6 +210,9 @@ class ModelRenderer: gradient_stops.append(lin_grad_point) segment_colors.append(color) + # Skip a point, unless next is last + i += 1 + (1 if (i + 2) < max_len else 0) + if len(segment_colors) < 2: draw_polygon(self._rect, self._track_vertices, rl.Color(255, 255, 255, 30)) return @@ -345,8 +339,10 @@ class ModelRenderer: @staticmethod def _map_val(x, x0, x1, y0, y1): - """Map value x from range [x0, x1] to range [y0, y1]""" - return y0 + (y1 - y0) * ((x - x0) / (x1 - x0)) if x1 != x0 else y0 + x = max(x0, min(x, x1)) + ra = x1 - x0 + rb = y1 - y0 + return (x - x0) * rb / ra + y0 if ra != 0 else y0 @staticmethod def _hsla_to_color(h, s, l, a): From ae5e87e91586c7f92e5181cfd976cb71c92bf132 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 29 May 2025 13:43:38 -0700 Subject: [PATCH 366/477] bump panda --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index d75699e6ab..4f227f88c8 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit d75699e6abf41636701775ba0eea8e07858599ad +Subproject commit 4f227f88c827d02188763d676aab941401be8212 From a385ed59cf6ef65a8e97aef79bf53421e40013a2 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 29 May 2025 14:24:39 -0700 Subject: [PATCH 367/477] Invalid LKAS setting alert: custom text per brand (#35377) * draft * use it * friendly * clean up * CC * Apply suggestions from code review Co-authored-by: Adeeb Shihadeh * Update selfdrive/selfdrived/events.py --------- Co-authored-by: Adeeb Shihadeh --- selfdrive/selfdrived/events.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index 6293deb2af..9e1892bd5f 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -359,6 +359,16 @@ def personality_changed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging return NormalPermanentAlert(f"Driving Personality: {personality}", duration=1.5) +def invalid_lkas_setting_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert: + text = "Toggle stock LKAS on or off to engage" + if CP.brand == "tesla": + text = "Switch to Traffic-Aware Cruise Control to engage" + elif CP.brand == "mazda": + text = "Enable your car's LKAS to engage" + elif CP.brand == "nissan": + text = "Disable your car's stock LKAS to engage" + return NormalPermanentAlert("Invalid LKAS setting", text) + EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # ********** events with no alerts ********** @@ -412,8 +422,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { }, EventName.invalidLkasSetting: { - ET.PERMANENT: NormalPermanentAlert("Invalid LKAS setting", - "Toggle stock LKAS on or off to engage"), + ET.PERMANENT: invalid_lkas_setting_alert, ET.NO_ENTRY: NoEntryAlert("Invalid LKAS setting"), }, From 0d527c2409b8db15a068b398de23a2ba52d5fc2a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 30 May 2025 11:51:15 +0800 Subject: [PATCH 368/477] system/ui: fix the issue of missing path segments (#35383) match c++ version --- system/ui/onroad/model_renderer.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index afed67aab2..141ffa3110 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -313,8 +313,7 @@ class ModelRenderer: line_y = line.y line_z = line.z - left_points: list[tuple[float, float]] = [] - right_points: list[tuple[float, float]] = [] + points = [] for i in range(max_idx + 1): # Skip points with negative x (behind camera) @@ -326,16 +325,16 @@ class ModelRenderer: if left and right: # Check for inversion when going over hills - if not allow_invert and left_points and left[1] > left_points[-1][1]: + if not allow_invert and points and left[1] > points[-1][1]: continue - left_points.append(left) - right_points.append(right) + points.append(left) + points.insert(0, right) - if not left_points or not right_points: + if not points: return np.empty((0, 2), dtype=np.float32) - return np.array(left_points + right_points[::-1], dtype=np.float32) + return np.array(points, dtype=np.float32) @staticmethod def _map_val(x, x0, x1, y0, y1): From 6e9e43d03bb9222555d54c5825f3aa9a8536ece7 Mon Sep 17 00:00:00 2001 From: Brett Sanderson Date: Fri, 30 May 2025 02:18:44 -0400 Subject: [PATCH 369/477] Fix CI for external repositories (#35382) --- .github/workflows/selfdrive_tests.yaml | 72 +++++++++++++++++--------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 3edb6c2981..8403bdd9cc 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -32,9 +32,12 @@ env: jobs: build_release: name: build release - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} env: STRIPPED_DIR: /tmp/releasepilot steps: @@ -67,9 +70,12 @@ jobs: run: release/check-submodules.sh build: - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} steps: - uses: actions/checkout@v4 with: @@ -118,9 +124,12 @@ jobs: static_analysis: name: static analysis - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} env: PYTHONWARNINGS: default steps: @@ -134,9 +143,12 @@ jobs: unit_tests: name: unit tests - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} steps: - uses: actions/checkout@v4 with: @@ -161,9 +173,12 @@ jobs: process_replay: name: process replay - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} steps: - uses: actions/checkout@v4 with: @@ -214,9 +229,12 @@ jobs: test_cars: name: cars - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} strategy: fail-fast: false matrix: @@ -306,9 +324,12 @@ jobs: simulator_driving: name: simulator driving - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} if: (github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) steps: - uses: actions/checkout@v4 @@ -328,9 +349,12 @@ jobs: create_ui_report: # This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml name: Create UI Report - runs-on: - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-amd64-8x16' || 'ubuntu-24.04' }} - - ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-experiments:docker.builds.local-cache=separate' || 'ubuntu-24.04' }} + runs-on: ${{ + (github.repository == 'commaai/openpilot') && + ((github.event_name != 'pull_request') || + (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) + && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') + || fromJSON('["ubuntu-24.04"]') }} if: false # FIXME: FrameReader is broken on CI runners steps: - uses: actions/checkout@v4 From 31e22b0a1d120eb1de4fc6ed38eb613431410aa1 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 30 May 2025 03:06:15 -0400 Subject: [PATCH 370/477] ui: add models panel (#956) * models panel * models panel * fix ui report * stupid scroll * cleanup whitespaces * Update selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc --------- Co-authored-by: DevTekVE --- selfdrive/ui/sunnypilot/SConscript | 1 + .../qt/offroad/settings/models_panel.cc | 12 ++++++++++++ .../qt/offroad/settings/models_panel.h | 17 +++++++++++++++++ .../sunnypilot/qt/offroad/settings/settings.cc | 2 ++ selfdrive/ui/tests/test_ui/run.py | 14 +++++++++++--- .../selfdrive/assets/offroad/icon_models.png | 3 +++ 6 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h create mode 100644 sunnypilot/selfdrive/assets/offroad/icon_models.png diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index d189751843..b5d18c5b91 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -24,6 +24,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/lateral_panel.cc", "sunnypilot/qt/offroad/settings/longitudinal_panel.cc", "sunnypilot/qt/offroad/settings/max_time_offroad.cc", + "sunnypilot/qt/offroad/settings/models_panel.cc", "sunnypilot/qt/offroad/settings/settings.cc", "sunnypilot/qt/offroad/settings/software_panel.cc", "sunnypilot/qt/offroad/settings/sunnylink_panel.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc new file mode 100644 index 0000000000..8f8d38d2d8 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h" + +ModelsPanel::ModelsPanel(QWidget *parent) : QWidget(parent) { + +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h new file mode 100644 index 0000000000..6c28b5bf35 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +class ModelsPanel : public QWidget { + Q_OBJECT + +public: + explicit ModelsPanel(QWidget *parent = nullptr); +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc index 48a395dad7..ad057ac714 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc @@ -13,6 +13,7 @@ #include "selfdrive/ui/sunnypilot/qt/network/networking.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h" @@ -79,6 +80,7 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { PanelInfo(" " + tr("sunnylink"), new SunnylinkPanel(this), "../assets/icons/wifi_strength_full.svg"), PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"), PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"), + PanelInfo(" " + tr("Models"), new ModelsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_models.png"), PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"), PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/icons/speed_limit.png"), PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"), diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 9d078fdd9e..f8c64de769 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -213,6 +213,12 @@ def setup_settings_sunnylink_sponsor_button(click, pm: PubMaster, scroll=None): click(1967, 225) time.sleep(UI_DELAY) +def setup_settings_models(click, pm: PubMaster, scroll=None): + setup_settings_device(click, pm) + click(278, 852) + time.sleep(UI_DELAY) + + def setup_settings_steering(click, pm: PubMaster, scroll=None): CP = car.CarParams() CP.carFingerprint = "HONDA_CIVIC" @@ -223,25 +229,26 @@ def setup_settings_steering(click, pm: PubMaster, scroll=None): Params().put("CarParamsSPPersistent", CP_SP.to_bytes()) setup_settings_device(click, pm) - click(278, 852) + click(278, 962) time.sleep(UI_DELAY) def setup_settings_steering_mads(click, pm: PubMaster, scroll=None): Params().put_bool("Mads", True) setup_settings_device(click, pm) - click(278, 852) + click(278, 962) click(970, 250) time.sleep(UI_DELAY) def setup_settings_steering_alc(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) - click(278, 852) + click(278, 962) click(970, 534) time.sleep(UI_DELAY) def setup_settings_driving(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) + scroll(-1, 278, 962) click(278, 962) time.sleep(UI_DELAY) @@ -295,6 +302,7 @@ CASES = { CASES.update({ "settings_sunnylink": setup_settings_sunnylink, "settings_sunnylink_sponsor_button": setup_settings_sunnylink_sponsor_button, + "settings_models": setup_settings_models, "settings_steering": setup_settings_steering, "settings_steering_mads": setup_settings_steering_mads, "settings_steering_alc": setup_settings_steering_alc, diff --git a/sunnypilot/selfdrive/assets/offroad/icon_models.png b/sunnypilot/selfdrive/assets/offroad/icon_models.png new file mode 100644 index 0000000000..0131759703 --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_models.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9b431c274a55437b5a65ada7c85503e137bc2ffa8917510ad9affb14a407d82 +size 14366 From d9d32ebea26955f3a535be33a44ded11026ae672 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 30 May 2025 06:06:50 -0400 Subject: [PATCH 371/477] UI: Add Search Capability to Branch Selector (#814) * Create sync-dev.yml * Update selfdrive_tests.yaml * Update sync-dev.yml * Update sync-dev.yml * Update sync-dev.yml * add search capability in branch selector * maybe i should remove test code * include * reduce needed includes * add newline for eof * no changes needed in upstream file * not sure why this was here * try to resolve merge conflicts for dev squash * try to resolve merge conflict * No need for this for the time being --------- Co-authored-by: Discountchubbs <159560811+Discountchubbs@users.noreply.github.com> Co-authored-by: Jason Wen Co-authored-by: DevTekVE --- .../qt/offroad/settings/software_panel.cc | 43 +++++++++++++++++++ .../qt/offroad/settings/software_panel.h | 4 ++ selfdrive/ui/sunnypilot/qt/util.cc | 32 ++++++++++++++ selfdrive/ui/sunnypilot/qt/util.h | 2 + 4 files changed, 81 insertions(+) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc index 20f3fdb598..57fb38c20b 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc @@ -17,6 +17,19 @@ * @param parent Parent widget */ SoftwarePanelSP::SoftwarePanelSP(QWidget *parent) : SoftwarePanel(parent) { + +// branch selector + QObject::disconnect(targetBranchBtn, nullptr, nullptr, nullptr); + connect(targetBranchBtn, &ButtonControlSP::clicked, [=]() { + InputDialog d(tr("Search Branch"), this, tr("Enter search keywords, or leave blank to list all branches."), false); + d.setMinLength(0); + const int ret = d.exec(); + if (ret) { + searchBranches(d.text()); + } + + }); + const auto current_model = GetActiveModelName(); currentModelLblBtn = new ButtonControlSP(tr("Current Model"), tr("SELECT"), current_model); currentModelLblBtn->setValue(current_model); @@ -205,3 +218,33 @@ void SoftwarePanelSP::showResetParamsDialog() { params.remove("LiveTorqueParameters"); } } + +/** + * @brief Searches for available branches based on a query string, presents the results in a dialog, + * and updates the target branch if a selection is made. + * + * This function filters the list of branches based on the provided query, and displays the filtered branches in a selection dialog. + * If a branch is selected, the "UpdaterTargetBranch" parameter is updated and a check for updates is triggered. + * If no branches are found matching the query, an alert dialog is displayed. + * + * @param query The search query string. + */ +void SoftwarePanelSP::searchBranches(const QString &query) { + + QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(","); + QStringList results = searchFromList(query, branches); + results.sort(); + + if (results.isEmpty()) { + ConfirmationDialog::alert(tr("No branches found for keywords: %1").arg(query), this); + return; + } + + QString selected_branch = MultiOptionDialog::getSelection(tr("Select a branch"), results, "", this); + + if (!selected_branch.isEmpty()) { + params.put("UpdaterTargetBranch", selected_branch.toStdString()); + targetBranchBtn->setValue(selected_branch); + checkForUpdates(); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h index d0299d6efd..3679ade410 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h @@ -9,6 +9,7 @@ #include #include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/util.h" #include "selfdrive/ui/qt/offroad/settings.h" class SoftwarePanelSP final : public SoftwarePanel { @@ -17,6 +18,9 @@ class SoftwarePanelSP final : public SoftwarePanel { public: explicit SoftwarePanelSP(QWidget *parent = nullptr); +private: + void searchBranches(const QString &query); + private: QString GetActiveModelName(); void updateModelManagerState(); diff --git a/selfdrive/ui/sunnypilot/qt/util.cc b/selfdrive/ui/sunnypilot/qt/util.cc index c729eaafd5..ca85935d0b 100644 --- a/selfdrive/ui/sunnypilot/qt/util.cc +++ b/selfdrive/ui/sunnypilot/qt/util.cc @@ -78,3 +78,35 @@ QMap loadPlatformList() { return _platforms; } + +/** + * @brief Searches a list of strings for elements containing all search terms in a query. + * + * The search is case-insensitive and normalizes both the query and the list elements + * using Unicode KD normalization before comparison. Non-alphanumeric characters are + * removed from the search terms before comparison. + * + * @param query The search query string. Multiple words can be separated by spaces. + * @param list The source list of strings to search. + * @return A list of strings from the input list that contain all of the search terms. + */ +QStringList searchFromList(const QString &query, const QStringList &list) { + if (query.isEmpty()) { + return list; + } + + QStringList search_terms = query.simplified().toLower().split(" ", QString::SkipEmptyParts); + QStringList search_results; + + for (const QString &element : list) { + if (std::all_of(search_terms.begin(), search_terms.end(), [&](const QString &term) { + QString normalized_term = term.normalized(QString::NormalizationForm_KD).toLower(); + normalized_term.remove(QRegularExpression("[^a-zA-Z0-9\\s]")); + QString normalized_element = element.normalized(QString::NormalizationForm_KD).toLower(); + return normalized_element.contains(normalized_term, Qt::CaseInsensitive); + })) { + search_results << element; + } + } + return search_results; +} diff --git a/selfdrive/ui/sunnypilot/qt/util.h b/selfdrive/ui/sunnypilot/qt/util.h index 0a581579e2..089b5370cc 100644 --- a/selfdrive/ui/sunnypilot/qt/util.h +++ b/selfdrive/ui/sunnypilot/qt/util.h @@ -12,9 +12,11 @@ #include #include +#include #include QString getUserAgent(bool sunnylink = false); std::optional getSunnylinkDongleId(); std::optional getParamIgnoringDefault(const std::string ¶m_name, const std::string &default_value); QMap loadPlatformList(); +QStringList searchFromList(const QString &query, const QStringList &list); From be406d8f838f58de21c746eccba73e705bf11841 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 30 May 2025 07:06:09 -0400 Subject: [PATCH 372/477] ui: move model selector to models panel (#957) * models panel * models panel * fix ui report * stupid scroll * move model selector to models panel * cleanup whitespaces * cleanup bad merge --------- Co-authored-by: DevTekVE --- .../qt/offroad/settings/models_panel.cc | 201 ++++++++++++++++++ .../qt/offroad/settings/models_panel.h | 47 ++++ .../qt/offroad/settings/software_panel.cc | 201 +----------------- .../qt/offroad/settings/software_panel.h | 44 ---- 4 files changed, 249 insertions(+), 244 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 8f8d38d2d8..d33cb550ce 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -5,8 +5,209 @@ * See the LICENSE.md file in the root directory for more details. */ +#include +#include + +#include "common/model.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" ModelsPanel::ModelsPanel(QWidget *parent) : QWidget(parent) { + QVBoxLayout *main_layout = new QVBoxLayout(this); + main_layout->setContentsMargins(50, 20, 50, 20); + ListWidgetSP *list = new ListWidgetSP(this); + ScrollViewSP *scroller = new ScrollViewSP(list, this); + main_layout->addWidget(scroller); + + const auto current_model = GetActiveModelName(); + currentModelLblBtn = new ButtonControlSP(tr("Current Model"), tr("SELECT"), current_model); + currentModelLblBtn->setValue(current_model); + + connect(currentModelLblBtn, &ButtonControlSP::clicked, this, &ModelsPanel::handleCurrentModelLblBtnClicked); + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { + is_onroad = !offroad; + updateLabels(); + }); + connect(uiStateSP(), &UIStateSP::uiUpdate, this, &ModelsPanel::updateLabels); + list->addItem(currentModelLblBtn); +} + + +/** + * @brief Updates the UI with bundle download progress information + * Reads status from modelManagerSP cereal message and displays status for all models + */ +void ModelsPanel::handleBundleDownloadProgress() { + using DS = cereal::ModelManagerSP::DownloadStatus; + if (!model_manager.hasSelectedBundle() && !model_manager.hasActiveBundle()) { + currentModelLblBtn->setDescription(tr("No custom model selected!")); + return; + } + + const bool showSelectedBundle = model_manager.hasSelectedBundle() && (isDownloading() || model_manager.getSelectedBundle().getStatus() == DS::FAILED); + const auto &bundle = showSelectedBundle ? model_manager.getSelectedBundle() : model_manager.getActiveBundle(); + const auto &models = bundle.getModels(); + download_status = bundle.getStatus(); + const auto download_status_changed = prev_download_status != download_status; + QStringList status; + + // Get status for each model type in order + for (const auto &model: models) { + QString typeName; + QString modelName = QString::fromStdString(bundle.getDisplayName()); + + switch (model.getType()) { + case cereal::ModelManagerSP::Model::Type::SUPERCOMBO: + typeName = tr("Driving"); + break; + case cereal::ModelManagerSP::Model::Type::NAVIGATION: + typeName = tr("Navigation"); + break; + case cereal::ModelManagerSP::Model::Type::VISION: + typeName = tr("Vision"); + break; + case cereal::ModelManagerSP::Model::Type::POLICY: + typeName = tr("Policy"); + break; + } + + const auto &progress = model.getArtifact().getDownloadProgress(); + QString line; + + if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING) { + line = tr("Downloading %1 model [%2]... (%3%)").arg(typeName, modelName).arg(progress.getProgress(), 0, 'f', 2); + } else if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADED) { + line = tr("%1 model [%2] %3").arg(typeName, modelName, download_status_changed ? tr("downloaded") : tr("ready")); + } else if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::CACHED) { + line = tr("%1 model [%2] %3").arg(typeName, modelName, download_status_changed ? tr("from cache") : tr("ready")); + } else if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::FAILED) { + line = tr("%1 model [%2] download failed").arg(typeName, modelName); + } else { + line = tr("%1 model [%2] pending...").arg(typeName, modelName); + } + status.append(line); + } + + currentModelLblBtn->setDescription(status.join("\n")); + + if (prev_download_status != download_status) { + switch (bundle.getStatus()) { + case cereal::ModelManagerSP::DownloadStatus::DOWNLOADING: + case cereal::ModelManagerSP::DownloadStatus::CACHED: + case cereal::ModelManagerSP::DownloadStatus::DOWNLOADED: + currentModelLblBtn->showDescription(); + break; + case cereal::ModelManagerSP::DownloadStatus::FAILED: + default: + break; + } + } + prev_download_status = download_status; +} + +/** + * @brief Gets the name of the currently selected model bundle + * @return Display name of the selected bundle or default model name + */ +QString ModelsPanel::GetActiveModelName() { + if (model_manager.hasActiveBundle()) { + return QString::fromStdString(model_manager.getActiveBundle().getDisplayName()); + } + + return DEFAULT_MODEL; +} + +void ModelsPanel::updateModelManagerState() { + const SubMaster &sm = *(uiStateSP()->sm); + model_manager = sm["modelManagerSP"].getModelManagerSP(); +} + +/** + * @brief Handles the model bundle selection button click + * Displays available bundles, allows selection, and initiates download + */ +void ModelsPanel::handleCurrentModelLblBtnClicked() { + currentModelLblBtn->setEnabled(false); + currentModelLblBtn->setValue(tr("Fetching models...")); + + // Create mapping of bundle indices to display names + QMap index_to_bundle; + const auto bundles = model_manager.getAvailableBundles(); + for (const auto &bundle: bundles) { + index_to_bundle.insert(bundle.getIndex(), QString::fromStdString(bundle.getDisplayName())); + } + + // Sort bundles by index in descending order + QStringList bundleNames; + // Add "Default" as the first option + bundleNames.append(tr("Use Default")); + + auto indices = index_to_bundle.keys(); + std::sort(indices.begin(), indices.end(), std::greater()); + for (const auto &index: indices) { + bundleNames.append(index_to_bundle[index]); + } + + currentModelLblBtn->setValue(GetActiveModelName()); + + const QString selectedBundleName = MultiOptionDialog::getSelection( + tr("Select a Model"), bundleNames, GetActiveModelName(), this); + + if (selectedBundleName.isEmpty() || !canContinueOnMeteredDialog()) { + return; + } + + // Handle "Stock" selection differently + if (selectedBundleName == tr("Use Default")) { + params.remove("ModelManager_ActiveBundle"); + currentModelLblBtn->setValue(tr("Default")); + showResetParamsDialog(); + } else { + // Find selected bundle and initiate download + for (const auto &bundle: bundles) { + if (QString::fromStdString(bundle.getDisplayName()) == selectedBundleName) { + params.put("ModelManager_DownloadIndex", std::to_string(bundle.getIndex())); + if (bundle.getGeneration() != model_manager.getActiveBundle().getGeneration()) { + showResetParamsDialog(); + } + break; + } + } + } + + updateLabels(); +} + +/** + * @brief Updates the UI elements based on current state + */ +void ModelsPanel::updateLabels() { + if (!isVisible()) { + return; + } + + updateModelManagerState(); + handleBundleDownloadProgress(); + currentModelLblBtn->setEnabled(!is_onroad && !isDownloading()); + currentModelLblBtn->setValue(GetActiveModelName()); +} + +/** + * @brief Shows dialog prompting user to reset calibration after model download + */ +void ModelsPanel::showResetParamsDialog() { + const auto confirmMsg = QString("%1

%2

%3") + .arg(tr("Model download has started in the background.")) + .arg(tr("We STRONGLY suggest you to reset calibration.")) + .arg(tr("Would you like to do that now?")); + const auto button_text = tr("Reset Calibration"); + + QString content("

" + tr("Driving Model Selector") + "


" + "

" + confirmMsg + "

"); + + if (showConfirmationDialog(content, button_text, false)) { + params.remove("CalibrationParams"); + params.remove("LiveTorqueParameters"); + } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h index 6c28b5bf35..4ba526651e 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h @@ -14,4 +14,51 @@ class ModelsPanel : public QWidget { public: explicit ModelsPanel(QWidget *parent = nullptr); + +private: + QString GetActiveModelName(); + void updateModelManagerState(); + + bool isDownloading() const { + if (!model_manager.hasSelectedBundle()) { + return false; + } + + const auto &selected_bundle = model_manager.getSelectedBundle(); + return selected_bundle.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING; + } + + // UI update related methods + void updateLabels(); + void handleCurrentModelLblBtnClicked(); + void handleBundleDownloadProgress(); + void showResetParamsDialog(); + cereal::ModelManagerSP::Reader model_manager; + cereal::ModelManagerSP::DownloadStatus download_status{}; + cereal::ModelManagerSP::DownloadStatus prev_download_status{}; + + bool canContinueOnMeteredDialog() { + if (!is_metered) return true; + return showConfirmationDialog(QString(), QString(), is_metered); + } + + inline bool showConfirmationDialog(const QString &message = QString(), const QString &confirmButtonText = QString(), const bool show_metered_warning = false) { + return showConfirmationDialog(this, message, confirmButtonText, show_metered_warning); + } + + static inline bool showConfirmationDialog(QWidget *parent, const QString &message = QString(), const QString &confirmButtonText = QString(), const bool show_metered_warning = false) { + const QString warning_message = show_metered_warning ? tr("Warning: You are on a metered connection!") : QString(); + const QString final_message = QString("%1%2").arg(!message.isEmpty() ? message + "\n" : QString(), warning_message); + const QString final_buttonText = !confirmButtonText.isEmpty() ? confirmButtonText : QString(tr("Continue") + " %1").arg(show_metered_warning ? tr("on Metered") : ""); + + return ConfirmationDialog(final_message, final_buttonText, tr("Cancel"), true, parent).exec(); + } + + bool is_metered{}; + bool is_wifi{}; + bool is_onroad = false; + + ButtonControlSP *currentModelLblBtn; + Params params; + }; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc index 57fb38c20b..9430d02ba8 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.cc @@ -7,18 +7,8 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h" -#include -#include - -#include "common/model.h" - -/** - * @brief Constructs the software panel with model bundle selection functionality - * @param parent Parent widget - */ SoftwarePanelSP::SoftwarePanelSP(QWidget *parent) : SoftwarePanel(parent) { - -// branch selector + // branch selector QObject::disconnect(targetBranchBtn, nullptr, nullptr, nullptr); connect(targetBranchBtn, &ButtonControlSP::clicked, [=]() { InputDialog d(tr("Search Branch"), this, tr("Enter search keywords, or leave blank to list all branches."), false); @@ -27,196 +17,7 @@ SoftwarePanelSP::SoftwarePanelSP(QWidget *parent) : SoftwarePanel(parent) { if (ret) { searchBranches(d.text()); } - }); - - const auto current_model = GetActiveModelName(); - currentModelLblBtn = new ButtonControlSP(tr("Current Model"), tr("SELECT"), current_model); - currentModelLblBtn->setValue(current_model); - - connect(currentModelLblBtn, &ButtonControlSP::clicked, this, &SoftwarePanelSP::handleCurrentModelLblBtnClicked); - QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &SoftwarePanelSP::updateLabels); - AddWidgetAt(0, currentModelLblBtn); -} - -/** - * @brief Updates the UI with bundle download progress information - * Reads status from modelManagerSP cereal message and displays status for all models - */ -void SoftwarePanelSP::handleBundleDownloadProgress() { - using DS = cereal::ModelManagerSP::DownloadStatus; - if (!model_manager.hasSelectedBundle() && !model_manager.hasActiveBundle()) { - currentModelLblBtn->setDescription(tr("No custom model selected!")); - return; - } - - const bool showSelectedBundle = model_manager.hasSelectedBundle() && (isDownloading() || model_manager.getSelectedBundle().getStatus() == DS::FAILED); - const auto &bundle = showSelectedBundle ? model_manager.getSelectedBundle() : model_manager.getActiveBundle(); - const auto &models = bundle.getModels(); - download_status = bundle.getStatus(); - const auto download_status_changed = prev_download_status != download_status; - QStringList status; - - // Get status for each model type in order - for (const auto &model: models) { - QString typeName; - QString modelName = QString::fromStdString(bundle.getDisplayName()); - - switch (model.getType()) { - case cereal::ModelManagerSP::Model::Type::SUPERCOMBO: - typeName = tr("Driving"); - break; - case cereal::ModelManagerSP::Model::Type::NAVIGATION: - typeName = tr("Navigation"); - break; - case cereal::ModelManagerSP::Model::Type::VISION: - typeName = tr("Vision"); - break; - case cereal::ModelManagerSP::Model::Type::POLICY: - typeName = tr("Policy"); - break; - } - - const auto &progress = model.getArtifact().getDownloadProgress(); - QString line; - - if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING) { - line = tr("Downloading %1 model [%2]... (%3%)").arg(typeName, modelName).arg(progress.getProgress(), 0, 'f', 2); - } else if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADED) { - line = tr("%1 model [%2] %3").arg(typeName, modelName, download_status_changed ? tr("downloaded") : tr("ready")); - } else if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::CACHED) { - line = tr("%1 model [%2] %3").arg(typeName, modelName, download_status_changed ? tr("from cache") : tr("ready")); - } else if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::FAILED) { - line = tr("%1 model [%2] download failed").arg(typeName, modelName); - } else { - line = tr("%1 model [%2] pending...").arg(typeName, modelName); - } - status.append(line); - } - - currentModelLblBtn->setDescription(status.join("\n")); - - if (prev_download_status != download_status) { - switch (bundle.getStatus()) { - case cereal::ModelManagerSP::DownloadStatus::DOWNLOADING: - case cereal::ModelManagerSP::DownloadStatus::CACHED: - case cereal::ModelManagerSP::DownloadStatus::DOWNLOADED: - currentModelLblBtn->showDescription(); - break; - case cereal::ModelManagerSP::DownloadStatus::FAILED: - default: - break; - } - } - prev_download_status = download_status; -} - -/** - * @brief Gets the name of the currently selected model bundle - * @return Display name of the selected bundle or default model name - */ -QString SoftwarePanelSP::GetActiveModelName() { - if (model_manager.hasActiveBundle()) { - return QString::fromStdString(model_manager.getActiveBundle().getDisplayName()); - } - - return DEFAULT_MODEL; -} - -void SoftwarePanelSP::updateModelManagerState() { - const SubMaster &sm = *(uiStateSP()->sm); - model_manager = sm["modelManagerSP"].getModelManagerSP(); -} - -/** - * @brief Handles the model bundle selection button click - * Displays available bundles, allows selection, and initiates download - */ -void SoftwarePanelSP::handleCurrentModelLblBtnClicked() { - currentModelLblBtn->setEnabled(false); - currentModelLblBtn->setValue(tr("Fetching models...")); - - // Create mapping of bundle indices to display names - QMap index_to_bundle; - const auto bundles = model_manager.getAvailableBundles(); - for (const auto &bundle: bundles) { - index_to_bundle.insert(bundle.getIndex(), QString::fromStdString(bundle.getDisplayName())); - } - - // Sort bundles by index in descending order - QStringList bundleNames; - // Add "Default" as the first option - bundleNames.append(tr("Use Default")); - - auto indices = index_to_bundle.keys(); - std::sort(indices.begin(), indices.end(), std::greater()); - for (const auto &index: indices) { - bundleNames.append(index_to_bundle[index]); - } - - currentModelLblBtn->setValue(GetActiveModelName()); - - const QString selectedBundleName = MultiOptionDialog::getSelection( - tr("Select a Model"), bundleNames, GetActiveModelName(), this); - - if (selectedBundleName.isEmpty() || !canContinueOnMeteredDialog()) { - return; - } - - // Handle "Stock" selection differently - if (selectedBundleName == tr("Use Default")) { - params.remove("ModelManager_ActiveBundle"); - currentModelLblBtn->setValue(tr("Default")); - showResetParamsDialog(); - } else { - // Find selected bundle and initiate download - for (const auto &bundle: bundles) { - if (QString::fromStdString(bundle.getDisplayName()) == selectedBundleName) { - params.put("ModelManager_DownloadIndex", std::to_string(bundle.getIndex())); - if (bundle.getGeneration() != model_manager.getActiveBundle().getGeneration()) { - showResetParamsDialog(); - } - break; - } - } - } - - updateLabels(); -} - -/** - * @brief Updates the UI elements based on current state - */ -void SoftwarePanelSP::updateLabels() { - if (!isVisible()) { - return; - } - - updateModelManagerState(); - handleBundleDownloadProgress(); - currentModelLblBtn->setEnabled(!is_onroad && !isDownloading()); - currentModelLblBtn->setValue(GetActiveModelName()); - - SoftwarePanel::updateLabels(); -} - -/** - * @brief Shows dialog prompting user to reset calibration after model download - */ -void SoftwarePanelSP::showResetParamsDialog() { - const auto confirmMsg = QString("%1

%2

%3") - .arg(tr("Model download has started in the background.")) - .arg(tr("We STRONGLY suggest you to reset calibration.")) - .arg(tr("Would you like to do that now?")); - const auto button_text = tr("Reset Calibration"); - - QString content("

" + tr("Driving Model Selector") + "


" - "

" + confirmMsg + "

"); - - if (showConfirmationDialog(content, button_text, false)) { - params.remove("CalibrationParams"); - params.remove("LiveTorqueParameters"); - } } /** diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h index 3679ade410..56d8b9be5b 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h @@ -7,7 +7,6 @@ #pragma once -#include #include "selfdrive/ui/sunnypilot/ui.h" #include "selfdrive/ui/sunnypilot/qt/util.h" #include "selfdrive/ui/qt/offroad/settings.h" @@ -20,47 +19,4 @@ public: private: void searchBranches(const QString &query); - -private: - QString GetActiveModelName(); - void updateModelManagerState(); - - bool isDownloading() const { - if (!model_manager.hasSelectedBundle()) { - return false; - } - - const auto &selected_bundle = model_manager.getSelectedBundle(); - return selected_bundle.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING; - } - - // UI update related methods - void updateLabels() override; - void handleCurrentModelLblBtnClicked(); - void handleBundleDownloadProgress(); - void showResetParamsDialog(); - cereal::ModelManagerSP::Reader model_manager; - cereal::ModelManagerSP::DownloadStatus download_status{}; - cereal::ModelManagerSP::DownloadStatus prev_download_status{}; - - bool canContinueOnMeteredDialog() { - if (!is_metered) return true; - return showConfirmationDialog(QString(), QString(), is_metered); - } - - inline bool showConfirmationDialog(const QString &message = QString(), const QString &confirmButtonText = QString(), const bool show_metered_warning = false) { - return showConfirmationDialog(this, message, confirmButtonText, show_metered_warning); - } - - static inline bool showConfirmationDialog(QWidget *parent, const QString &message = QString(), const QString &confirmButtonText = QString(), const bool show_metered_warning = false) { - const QString warning_message = show_metered_warning ? tr("Warning: You are on a metered connection!") : QString(); - const QString final_message = QString("%1%2").arg(!message.isEmpty() ? message + "\n" : QString(), warning_message); - const QString final_buttonText = !confirmButtonText.isEmpty() ? confirmButtonText : QString(tr("Continue") + " %1").arg(show_metered_warning ? tr("on Metered") : ""); - - return ConfirmationDialog(final_message, final_buttonText, tr("Cancel"), true, parent).exec(); - } - - bool is_metered{}; - bool is_wifi{}; - ButtonControlSP *currentModelLblBtn; }; From ff5b587fd17dee1070086af8a62da42bc33302b6 Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Fri, 30 May 2025 13:14:30 +0200 Subject: [PATCH 373/477] ui: Bumping the total scons nodes for progress bar (#965) Bumping the total scons nodes --- system/manager/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/manager/build.py b/system/manager/build.py index 771024794f..49c8128660 100755 --- a/system/manager/build.py +++ b/system/manager/build.py @@ -14,7 +14,7 @@ from openpilot.system.version import get_build_metadata MAX_CACHE_SIZE = 4e9 if "CI" in os.environ else 2e9 CACHE_DIR = Path("/data/scons_cache" if AGNOS else "/tmp/scons_cache") -TOTAL_SCONS_NODES = 3130 +TOTAL_SCONS_NODES = 3765 MAX_BUILD_PROGRESS = 100 def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: From e42044b8339c04bbe609a70e7b8a9a4f25a20597 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 30 May 2025 22:36:05 +0800 Subject: [PATCH 374/477] system/ui: fix lint error (#35387) fix lint error --- system/ui/onroad/model_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index 141ffa3110..ea5363db79 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -313,7 +313,7 @@ class ModelRenderer: line_y = line.y line_z = line.z - points = [] + points: list = [] for i in range(max_idx + 1): # Skip points with negative x (behind camera) From b8f3e7bcf089959f788f024d1772a5575dc97291 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 30 May 2025 22:37:07 +0800 Subject: [PATCH 375/477] system/ui: improve road view with driving state border and clipping (#35385) improve road view with driving state border and clipping --- system/ui/onroad/augmented_road_view.py | 54 ++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 316167a828..b4b6eef12c 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -1,5 +1,6 @@ import numpy as np import pyray as rl +from enum import Enum from cereal import messaging, log from msgq.visionipc import VisionStreamType @@ -10,8 +11,15 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS, DeviceCamera from openpilot.common.transformations.orientation import rot_from_euler +OpState = log.SelfdriveState.OpenpilotState CALIBRATED = log.LiveCalibrationData.Status.calibrated DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"] +UI_BORDER_SIZE = 30 + +class BorderStatus(Enum): + DISENGAGED = rl.Color(0x17, 0x33, 0x49, 0xc8) # Blue for disengaged state + OVERRIDE = rl.Color(0x91, 0x9b, 0x95, 0xf1) # Gray for override state + ENGAGED = rl.Color(0x17, 0x86, 0x44, 0xf1) # Green for engaged state class AugmentedRoadView(CameraView): @@ -29,6 +37,7 @@ class AugmentedRoadView(CameraView): self._last_calib_time: float = 0 self._last_rect_dims = (0.0, 0.0) self._cached_matrix: np.ndarray | None = None + self._content_rect = rl.Rectangle() self.model_renderer = ModelRenderer() @@ -36,6 +45,26 @@ class AugmentedRoadView(CameraView): # Update calibration before rendering self._update_calibration() + # Create inner content area with border padding + self._content_rect = rl.Rectangle( + rect.x + UI_BORDER_SIZE, + rect.y + UI_BORDER_SIZE, + rect.width - 2 * UI_BORDER_SIZE, + rect.height - 2 * UI_BORDER_SIZE, + ) + + # Draw colored border based on driving state + self._draw_border(rect) + + # Enable scissor mode to clip all rendering within content rectangle boundaries + # This creates a rendering viewport that prevents graphics from drawing outside the border + rl.begin_scissor_mode( + int(self._content_rect.x), + int(self._content_rect.y), + int(self._content_rect.width), + int(self._content_rect.height) + ) + # Render the base camera view super().render(rect) @@ -44,7 +73,21 @@ class AugmentedRoadView(CameraView): # - Path prediction # - Lead vehicle indicators # - Additional features - self.model_renderer.draw(rect, self.sm) + self.model_renderer.draw(self._content_rect, self.sm) + + # End clipping region + rl.end_scissor_mode() + + def _draw_border(self, rect: rl.Rectangle): + state = self.sm["selfdriveState"] + if state.state in (OpState.preEnabled, OpState.overriding): + status = BorderStatus.OVERRIDE + elif state.enabled: + status = BorderStatus.ENGAGED + else: + status = BorderStatus.DISENGAGED + + rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, status.value) def _update_calibration(self): # Update device camera if not already set @@ -71,7 +114,7 @@ class AugmentedRoadView(CameraView): def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: # Check if we can use cached matrix calib_time = self.sm.recv_frame.get('liveCalibration', 0) - current_dims = (rect.width, rect.height) + current_dims = (self._content_rect.width, self._content_rect.height) if (self._last_calib_time == calib_time and self._last_rect_dims == current_dims and self._cached_matrix is not None): @@ -89,7 +132,8 @@ class AugmentedRoadView(CameraView): kep = calib_transform @ inf_point # Calculate center points and dimensions - w, h = current_dims + x, y = self._content_rect.x, self._content_rect.y + w, h = self._content_rect.width, self._content_rect.height cx, cy = intrinsic[0, 2], intrinsic[1, 2] # Calculate max allowed offsets with margins @@ -117,8 +161,8 @@ class AugmentedRoadView(CameraView): ]) video_transform = np.array([ - [zoom, 0.0, (w / 2 - x_offset) - (cx * zoom)], - [0.0, zoom, (h / 2 - y_offset) - (cy * zoom)], + [zoom, 0.0, (w / 2 + x - x_offset) - (cx * zoom)], + [0.0, zoom, (h / 2 + y - y_offset) - (cy * zoom)], [0.0, 0.0, 1.0] ]) self.model_renderer.set_transform(video_transform @ calib_transform) From 29010cae23db80405a0ce4e3d773d4bb9569c198 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 00:34:56 +0800 Subject: [PATCH 376/477] system/ui: optimize ModelRenderer (#35369) * optimize ModelRenderer with vectorized operations * pre-calculate the exp mode colors * cleanup * improve batch map line to polygon * pre-calc leads --- system/ui/onroad/model_renderer.py | 407 ++++++++++++++++------------- 1 file changed, 224 insertions(+), 183 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index ea5363db79..faf538c58d 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -1,8 +1,8 @@ import colorsys -import bisect import numpy as np import pyray as rl from cereal import messaging, car +from dataclasses import dataclass, field from openpilot.common.params import Params from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.shader_polygon import draw_polygon @@ -27,6 +27,18 @@ NO_THROTTLE_COLORS = [ ] +@dataclass +class ModelPoints: + raw_points: np.ndarray = field(default_factory=lambda: np.empty((0, 3), dtype=np.float32)) + projected_points: np.ndarray = field(default_factory=lambda: np.empty((0, 2), dtype=np.float32)) + +@dataclass +class LeadVehicle: + glow: list[float] = field(default_factory=list) + chevron: list[float] = field(default_factory=list) + fill_alpha: int = 0 + + class ModelRenderer: def __init__(self): self._longitudinal_control = False @@ -35,28 +47,34 @@ class ModelRenderer: self._prev_allow_throttle = True self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) + self._lead_vehicles = [LeadVehicle(), LeadVehicle()] self._path_offset_z = 1.22 - # Initialize empty polygon vertices - self._track_vertices = np.empty((0, 2), dtype=np.float32) - self._lane_line_vertices = [np.empty((0, 2), dtype=np.float32) for _ in range(4)] - self._road_edge_vertices = [np.empty((0, 2), dtype=np.float32) for _ in range(2)] - self._lead_vertices = [None, None] + # Initialize ModelPoints objects + self._path = ModelPoints() + self._lane_lines = [ModelPoints() for _ in range(4)] + self._road_edges = [ModelPoints() for _ in range(2)] + self._acceleration_x = np.empty((0,), dtype=np.float32) # Transform matrix (3x3 for car space to screen space) - self._car_space_transform = np.zeros((3, 3)) + self._car_space_transform = np.zeros((3, 3), dtype=np.float32) self._transform_dirty = True self._clip_region = None self._rect = None + self._exp_gradient = { + 'start': (0.0, 1.0), # Bottom of path + 'end': (0.0, 0.0), # Top of path + 'colors': [], + 'stops': [], + } # Get longitudinal control setting from car parameters - car_params = Params().get("CarParams") - if car_params: + if car_params := Params().get("CarParams"): cp = messaging.log_from_bytes(car_params, car.CarParams) self._longitudinal_control = cp.openpilotLongitudinalControl def set_transform(self, transform: np.ndarray): - self._car_space_transform = transform + self._car_space_transform = transform.astype(np.float32) self._transform_dirty = True def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster): @@ -70,161 +88,189 @@ class ModelRenderer: rect.x - CLIP_MARGIN, rect.y - CLIP_MARGIN, rect.width + 2 * CLIP_MARGIN, rect.height + 2 * CLIP_MARGIN ) - # Update flags based on car state + # Update state self._experimental_mode = sm['selfdriveState'].experimentalMode self._path_offset_z = sm['liveCalibration'].height[0] if sm.updated['carParams']: self._longitudinal_control = sm['carParams'].openpilotLongitudinalControl - # Get model and radar data model = sm['modelV2'] radar_state = sm['radarState'] if sm.valid['radarState'] else None lead_one = radar_state.leadOne if radar_state else None render_lead_indicator = self._longitudinal_control and radar_state is not None # Update model data when needed - if self._transform_dirty or sm.updated['modelV2'] or sm.updated['radarState']: - self._update_model(model, lead_one) + model_updated = sm.updated['modelV2'] + if model_updated or sm.updated['radarState'] or self._transform_dirty: + if model_updated: + self._update_raw_points(model) + + pos_x_array = self._path.raw_points[:, 0] + if pos_x_array.size == 0: + return + + self._update_model(lead_one, pos_x_array) if render_lead_indicator: - self._update_leads(radar_state, model.position) + self._update_leads(radar_state, pos_x_array) self._transform_dirty = False + # Draw elements self._draw_lane_lines() - self._draw_path(sm, model, rect.height) + self._draw_path(sm) - # Draw lead vehicles if available if render_lead_indicator and radar_state: - lead_two = radar_state.leadTwo + self._draw_lead_indicator() - if lead_one and lead_one.status: - self._draw_lead(lead_one, self._lead_vertices[0], rect) + def _update_raw_points(self, model): + """Update raw 3D points from model data""" + self._path.raw_points = np.array([model.position.x, model.position.y, model.position.z], dtype=np.float32).T - if lead_two and lead_two.status and lead_one and (abs(lead_one.dRel - lead_two.dRel) > 3.0): - self._draw_lead(lead_two, self._lead_vertices[1], rect) + for i, lane_line in enumerate(model.laneLines): + self._lane_lines[i].raw_points = np.array([lane_line.x, lane_line.y, lane_line.z], dtype=np.float32).T - def _update_leads(self, radar_state, line): + for i, road_edge in enumerate(model.roadEdges): + self._road_edges[i].raw_points = np.array([road_edge.x, road_edge.y, road_edge.z], dtype=np.float32).T + + self._lane_line_probs = np.array(model.laneLineProbs, dtype=np.float32) + self._road_edge_stds = np.array(model.roadEdgeStds, dtype=np.float32) + self._acceleration_x = np.array(model.acceleration.x, dtype=np.float32) + + def _update_leads(self, radar_state, pos_x_array): """Update positions of lead vehicles""" + self._lead_vehicles = [LeadVehicle(), LeadVehicle()] leads = [radar_state.leadOne, radar_state.leadTwo] for i, lead_data in enumerate(leads): if lead_data and lead_data.status: - d_rel = lead_data.dRel - y_rel = lead_data.yRel - idx = self._get_path_length_idx(line, d_rel) - z = line.z[idx] - self._lead_vertices[i] = self._map_to_screen(d_rel, -y_rel, z + self._path_offset_z) + d_rel, y_rel, v_rel = lead_data.dRel, lead_data.yRel, lead_data.vRel + idx = self._get_path_length_idx(pos_x_array, d_rel) + z = self._path.raw_points[idx, 2] if idx < len(self._path.raw_points) else 0.0 + point = self._map_to_screen(d_rel, -y_rel, z + self._path_offset_z) + if point: + self._lead_vehicles[i] = self._update_lead_vehicle(d_rel, v_rel, point, self._rect) - def _update_model(self, model, lead): + def _update_model(self, lead, pos_x_array): """Update model visualization data based on model message""" - model_position = model.position + max_distance = np.clip(pos_x_array[-1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE) + max_idx = self._get_path_length_idx(pos_x_array, max_distance) - # Determine max distance to render - max_distance = np.clip(model_position.x[-1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE) - - # Update lane lines - lane_lines = model.laneLines - line_probs = model.laneLineProbs - max_idx = self._get_path_length_idx(lane_lines[0], max_distance) - - for i in range(4): - self._lane_line_probs[i] = line_probs[i] - self._lane_line_vertices[i] = self._map_line_to_polygon( - lane_lines[i], 0.025 * self._lane_line_probs[i], 0, max_idx + # Update lane lines using raw points + for i, lane_line in enumerate(self._lane_lines): + lane_line.projected_points = self._map_line_to_polygon( + lane_line.raw_points, 0.025 * self._lane_line_probs[i], 0.0, max_idx ) - # Update road edges - road_edges = model.roadEdges - edge_stds = model.roadEdgeStds + # Update road edges using raw points + for road_edge in self._road_edges: + road_edge.projected_points = self._map_line_to_polygon(road_edge.raw_points, 0.025, 0.0, max_idx) - for i in range(2): - self._road_edge_stds[i] = edge_stds[i] - self._road_edge_vertices[i] = self._map_line_to_polygon(road_edges[i], 0.025, 0, max_idx) - - # Update path + # Update path using raw points if lead and lead.status: lead_d = lead.dRel * 2.0 max_distance = np.clip(lead_d - min(lead_d * 0.35, 10.0), 0.0, max_distance) - max_idx = self._get_path_length_idx(model_position, max_distance) + max_idx = self._get_path_length_idx(pos_x_array, max_distance) - self._track_vertices = self._map_line_to_polygon(model_position, 0.9, self._path_offset_z, max_idx, False) + self._path.projected_points = self._map_line_to_polygon( + self._path.raw_points, 0.9, self._path_offset_z, max_idx, allow_invert=False + ) + + self._update_experimental_gradient(self._rect.height) + + def _update_experimental_gradient(self, height): + """Pre-calculate experimental mode gradient colors""" + if not self._experimental_mode: + return + + max_len = min(len(self._path.projected_points) // 2, len(self._acceleration_x)) + + segment_colors = [] + gradient_stops = [] + + i = 0 + while i < max_len: + track_idx = max_len - i - 1 # flip idx to start from bottom right + track_y = self._path.projected_points[track_idx][1] + if track_y < 0 or track_y > height: + i += 1 + continue + + # Calculate color based on acceleration + lin_grad_point = (height - track_y) / height + + # speed up: 120, slow down: 0 + path_hue = max(min(60 + self._acceleration_x[i] * 35, 120), 0) + path_hue = int(path_hue * 100 + 0.5) / 100 + + saturation = min(abs(self._acceleration_x[i] * 1.5), 1) + lightness = self._map_val(saturation, 0.0, 1.0, 0.95, 0.62) + alpha = self._map_val(lin_grad_point, 0.75 / 2.0, 0.75, 0.4, 0.0) + + # Use HSL to RGB conversion + color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha) + + gradient_stops.append(lin_grad_point) + segment_colors.append(color) + + # Skip a point, unless next is last + i += 1 + (1 if (i + 2) < max_len else 0) + + # Store the gradient in the path object + self._exp_gradient['colors'] = segment_colors + self._exp_gradient['stops'] = gradient_stops + + def _update_lead_vehicle(self, d_rel, v_rel, point, rect): + speed_buff, lead_buff = 10.0, 40.0 + + # Calculate fill alpha + fill_alpha = 0 + if d_rel < lead_buff: + fill_alpha = 255 * (1.0 - (d_rel / lead_buff)) + if v_rel < 0: + fill_alpha += 255 * (-1 * (v_rel / speed_buff)) + fill_alpha = min(fill_alpha, 255) + + # Calculate size and position + sz = np.clip((25 * 30) / (d_rel / 3 + 30), 15.0, 30.0) * 2.35 + x = np.clip(point[0], 0.0, rect.width - sz / 2) + y = min(point[1], rect.height - sz * 0.6) + + g_xo = sz / 5 + g_yo = sz / 10 + + glow = [(x + (sz * 1.35) + g_xo, y + sz + g_yo), (x, y - g_yo), (x - (sz * 1.35) - g_xo, y + sz + g_yo)] + chevron = [(x + (sz * 1.25), y + sz), (x, y), (x - (sz * 1.25), y + sz)] + + return LeadVehicle(glow=glow,chevron=chevron, fill_alpha=int(fill_alpha)) def _draw_lane_lines(self): """Draw lane lines and road edges""" - for i, vertices in enumerate(self._lane_line_vertices): - # Skip if no vertices - if vertices.size == 0: + for i, lane_line in enumerate(self._lane_lines): + if lane_line.projected_points.size == 0: continue - # Draw lane line alpha = np.clip(self._lane_line_probs[i], 0.0, 0.7) color = rl.Color(255, 255, 255, int(alpha * 255)) - draw_polygon(self._rect, vertices, color) + draw_polygon(self._rect, lane_line.projected_points, color) - for i, vertices in enumerate(self._road_edge_vertices): - # Skip if no vertices - if vertices.size == 0: + for i, road_edge in enumerate(self._road_edges): + if road_edge.projected_points.size == 0: continue - # Draw road edge alpha = np.clip(1.0 - self._road_edge_stds[i], 0.0, 1.0) color = rl.Color(255, 0, 0, int(alpha * 255)) - draw_polygon(self._rect, vertices, color) + draw_polygon(self._rect, road_edge.projected_points, color) - def _draw_path(self, sm, model, height): - """Draw the path polygon with gradient based on acceleration""" - if self._track_vertices.size == 0: + def _draw_path(self, sm): + """Draw path with dynamic coloring based on mode and throttle state.""" + if not self._path.projected_points.size: return if self._experimental_mode: # Draw with acceleration coloring - acceleration = model.acceleration.x - max_len = min(len(self._track_vertices) // 2, len(acceleration)) - - # Create segments for gradient coloring - segment_colors = [] - gradient_stops = [] - - i = 0 - while i < max_len: - track_idx = max_len - i - 1 # flip idx to start from bottom right - track_y = self._track_vertices[track_idx][1] - if track_y < 0 or track_y > height: - i += 1 - continue - - # Calculate color based on acceleration - lin_grad_point = (height - track_y) / height - - # speed up: 120, slow down: 0 - path_hue = max(min(60 + acceleration[i] * 35, 120), 0) - path_hue = int(path_hue * 100 + 0.5) / 100 - - saturation = min(abs(acceleration[i] * 1.5), 1) - lightness = self._map_val(saturation, 0.0, 1.0, 0.95, 0.62) - alpha = self._map_val(lin_grad_point, 0.75 / 2.0, 0.75, 0.4, 0.0) - - # Use HSL to RGB conversion - color = self._hsla_to_color(path_hue / 360.0, saturation, lightness, alpha) - - # Create quad segment - gradient_stops.append(lin_grad_point) - segment_colors.append(color) - - # Skip a point, unless next is last - i += 1 + (1 if (i + 2) < max_len else 0) - - if len(segment_colors) < 2: - draw_polygon(self._rect, self._track_vertices, rl.Color(255, 255, 255, 30)) - return - - # Create gradient specification - gradient = { - 'start': (0.0, 1.0), # Bottom of path - 'end': (0.0, 0.0), # Top of path - 'colors': segment_colors, - 'stops': gradient_stops, - } - draw_polygon(self._rect, self._track_vertices, gradient=gradient) + if len(self._exp_gradient['colors']) > 2: + draw_polygon(self._rect, self._path.projected_points, gradient=self._exp_gradient) + else: + draw_polygon(self._rect, self._path.projected_points, rl.Color(255, 255, 255, 30)) else: # Draw with throttle/no throttle gradient allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control @@ -249,46 +295,22 @@ class ModelRenderer: 'colors': blended_colors, 'stops': [0.0, 0.5, 1.0], } - draw_polygon(self._rect, self._track_vertices, gradient=gradient) + draw_polygon(self._rect, self._path.projected_points, gradient=gradient) - def _draw_lead(self, lead_data, vd, rect): - """Draw lead vehicle indicator""" - if not vd: - return + def _draw_lead_indicator(self): + # Draw lead vehicles if available + for lead in self._lead_vehicles: + if not lead.glow or not lead.chevron: + continue - speed_buff = 10.0 - lead_buff = 40.0 - d_rel = lead_data.dRel - v_rel = lead_data.vRel - - # Calculate fill alpha - fill_alpha = 0 - if d_rel < lead_buff: - fill_alpha = 255 * (1.0 - (d_rel / lead_buff)) - if v_rel < 0: - fill_alpha += 255 * (-1 * (v_rel / speed_buff)) - fill_alpha = min(fill_alpha, 255) - - # Calculate size and position - sz = np.clip((25 * 30) / (d_rel / 3 + 30), 15.0, 30.0) * 2.35 - x = np.clip(vd[0], 0.0, rect.width - sz / 2) - y = min(vd[1], rect.height - sz * 0.6) - - g_xo = sz / 5 - g_yo = sz / 10 - - # Draw glow - glow = [(x + (sz * 1.35) + g_xo, y + sz + g_yo), (x, y - g_yo), (x - (sz * 1.35) - g_xo, y + sz + g_yo)] - rl.draw_triangle_fan(glow, len(glow), rl.Color(218, 202, 37, 255)) - - # Draw chevron - chevron = [(x + (sz * 1.25), y + sz), (x, y), (x - (sz * 1.25), y + sz)] - rl.draw_triangle_fan(chevron, len(chevron), rl.Color(201, 34, 49, int(fill_alpha))) + rl.draw_triangle_fan(lead.glow, len(lead.glow), rl.Color(218, 202, 37, 255)) + rl.draw_triangle_fan(lead.chevron, len(lead.chevron), rl.Color(201, 34, 49, lead.fill_alpha)) @staticmethod - def _get_path_length_idx(line, path_height): + def _get_path_length_idx(pos_x_array: np.ndarray, path_height: float) -> int: """Get the index corresponding to the given path height""" - return bisect.bisect_right(line.x, path_height) - 1 + idx = np.searchsorted(pos_x_array, path_height, side='right') + return int(np.clip(idx - 1, 0, len(pos_x_array) - 1)) def _map_to_screen(self, in_x, in_y, in_z): """Project a point in car space to screen space""" @@ -298,43 +320,71 @@ class ModelRenderer: if abs(pt[2]) < 1e-6: return None - x = pt[0] / pt[2] - y = pt[1] / pt[2] + x, y = pt[0] / pt[2], pt[1] / pt[2] clip = self._clip_region - if x < clip.x or x > clip.x + clip.width or y < clip.y or y > clip.y + clip.height: + if not (clip.x <= x <= clip.x + clip.width and clip.y <= y <= clip.y + clip.height): return None return (x, y) - def _map_line_to_polygon(self, line, y_off, z_off, max_idx, allow_invert=True)-> np.ndarray: - """Convert a 3D line to a 2D polygon for drawing""" - line_x = line.x - line_y = line.y - line_z = line.z + def _map_line_to_polygon(self, line: np.ndarray, y_off: float, z_off: float, max_idx: int, allow_invert: bool = True) -> np.ndarray: + """Convert 3D line to 2D polygon for rendering.""" + if line.shape[0] == 0: + return np.empty((0, 2), dtype=np.float32) - points: list = [] + # Slice points and filter non-negative x-coordinates + points = line[:max_idx + 1][line[:max_idx + 1, 0] >= 0] + if points.shape[0] == 0: + return np.empty((0, 2), dtype=np.float32) - for i in range(max_idx + 1): - # Skip points with negative x (behind camera) - if line_x[i] < 0: - continue + # Create left and right 3D points in one array + n_points = points.shape[0] + points_3d = np.empty((n_points * 2, 3), dtype=np.float32) + points_3d[:n_points, 0] = points_3d[n_points:, 0] = points[:, 0] + points_3d[:n_points, 1] = points[:, 1] - y_off + points_3d[n_points:, 1] = points[:, 1] + y_off + points_3d[:n_points, 2] = points_3d[n_points:, 2] = points[:, 2] + z_off - left = self._map_to_screen(line_x[i], line_y[i] - y_off, line_z[i] + z_off) - right = self._map_to_screen(line_x[i], line_y[i] + y_off, line_z[i] + z_off) + # Single matrix multiplication for projections + proj = self._car_space_transform @ points_3d.T + valid_z = np.abs(proj[2]) > 1e-6 + if not np.any(valid_z): + return np.empty((0, 2), dtype=np.float32) - if left and right: - # Check for inversion when going over hills - if not allow_invert and points and left[1] > points[-1][1]: - continue + # Compute screen coordinates + screen = proj[:2, valid_z] / proj[2, valid_z][None, :] + left_screen = screen[:, :n_points].T + right_screen = screen[:, n_points:].T - points.append(left) - points.insert(0, right) + # Ensure consistent shapes by re-aligning valid points + valid_points = np.minimum(left_screen.shape[0], right_screen.shape[0]) + if valid_points == 0: + return np.empty((0, 2), dtype=np.float32) + left_screen = left_screen[:valid_points] + right_screen = right_screen[:valid_points] - if not points: - return np.empty((0, 2), dtype=np.float32) + if self._clip_region: + clip = self._clip_region + bounds_mask = ( + (left_screen[:, 0] >= clip.x) & (left_screen[:, 0] <= clip.x + clip.width) & + (left_screen[:, 1] >= clip.y) & (left_screen[:, 1] <= clip.y + clip.height) & + (right_screen[:, 0] >= clip.x) & (right_screen[:, 0] <= clip.x + clip.width) & + (right_screen[:, 1] >= clip.y) & (right_screen[:, 1] <= clip.y + clip.height) + ) + if not np.any(bounds_mask): + return np.empty((0, 2), dtype=np.float32) + left_screen = left_screen[bounds_mask] + right_screen = right_screen[bounds_mask] - return np.array(points, dtype=np.float32) + if not allow_invert and left_screen.shape[0] > 1: + keep = np.concatenate(([True], np.diff(left_screen[:, 1]) < 0)) + left_screen = left_screen[keep] + right_screen = right_screen[keep] + if left_screen.shape[0] == 0: + return np.empty((0, 2), dtype=np.float32) + + return np.vstack((left_screen, right_screen[::-1])).astype(np.float32) @staticmethod def _map_val(x, x0, x1, y0, y1): @@ -345,17 +395,8 @@ class ModelRenderer: @staticmethod def _hsla_to_color(h, s, l, a): - """Convert HSLA color to Raylib Color using colorsys module""" - # colorsys uses HLS format (Hue, Lightness, Saturation) - r, g, b = colorsys.hls_to_rgb(h, l, s) - - # Ensure values are in valid range - r_val = max(0, min(255, int(r * 255))) - g_val = max(0, min(255, int(g * 255))) - b_val = max(0, min(255, int(b * 255))) - a_val = max(0, min(255, int(a * 255))) - - return rl.Color(r_val, g_val, b_val, a_val) + r, g, b = [max(0, min(255, int(v * 255))) for v in colorsys.hls_to_rgb(h, l, s)] + return rl.Color(r, g, b, max(0, min(255, int(a * 255)))) @staticmethod def _blend_colors(begin_colors, end_colors, t): From e6eef5d9d01d7b75d56f58a964282d6f839371e5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 00:35:10 +0800 Subject: [PATCH 377/477] system/ui: implement driver monitoring UI (#35358) * pyui_driver_state_reander * ddd * draw_spline_linear * pre-calculate the face keypoints transform * remove int convert * improve * use draw_spline_linear * pre-calc points * state updated * render to texture * Revert "render to texture" This reverts commit 27be710f4c7aca3bb05e94ad69635d292e799ff4. * cleanup * dd * new dataclass * cleanup * use content_rect --- system/ui/onroad/augmented_road_view.py | 3 + system/ui/onroad/driver_state.py | 238 ++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 system/ui/onroad/driver_state.py diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index b4b6eef12c..67f6bb99a9 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -4,6 +4,7 @@ from enum import Enum from cereal import messaging, log from msgq.visionipc import VisionStreamType +from openpilot.system.ui.onroad.driver_state import DriverStateRenderer from openpilot.system.ui.onroad.model_renderer import ModelRenderer from openpilot.system.ui.widgets.cameraview import CameraView from openpilot.system.ui.lib.application import gui_app @@ -40,6 +41,7 @@ class AugmentedRoadView(CameraView): self._content_rect = rl.Rectangle() self.model_renderer = ModelRenderer() + self.driver_state_renderer = DriverStateRenderer() def render(self, rect): # Update calibration before rendering @@ -74,6 +76,7 @@ class AugmentedRoadView(CameraView): # - Lead vehicle indicators # - Additional features self.model_renderer.draw(self._content_rect, self.sm) + self.driver_state_renderer.draw(self._content_rect, self.sm) # End clipping region rl.end_scissor_mode() diff --git a/system/ui/onroad/driver_state.py b/system/ui/onroad/driver_state.py new file mode 100644 index 0000000000..4ad35bfaa5 --- /dev/null +++ b/system/ui/onroad/driver_state.py @@ -0,0 +1,238 @@ +import numpy as np +import pyray as rl +from dataclasses import dataclass +from openpilot.system.ui.lib.application import gui_app + +# Default 3D coordinates for face keypoints as a NumPy array +DEFAULT_FACE_KPTS_3D = np.array([ + [-5.98, -51.20, 8.00], [-17.64, -49.14, 8.00], [-23.81, -46.40, 8.00], [-29.98, -40.91, 8.00], + [-32.04, -37.49, 8.00], [-34.10, -32.00, 8.00], [-36.16, -21.03, 8.00], [-36.16, 6.40, 8.00], + [-35.47, 10.51, 8.00], [-32.73, 19.43, 8.00], [-29.30, 26.29, 8.00], [-24.50, 33.83, 8.00], + [-19.01, 41.37, 8.00], [-14.21, 46.17, 8.00], [-12.16, 47.54, 8.00], [-4.61, 49.60, 8.00], + [4.99, 49.60, 8.00], [12.53, 47.54, 8.00], [14.59, 46.17, 8.00], [19.39, 41.37, 8.00], + [24.87, 33.83, 8.00], [29.67, 26.29, 8.00], [33.10, 19.43, 8.00], [35.84, 10.51, 8.00], + [36.53, 6.40, 8.00], [36.53, -21.03, 8.00], [34.47, -32.00, 8.00], [32.42, -37.49, 8.00], + [30.36, -40.91, 8.00], [24.19, -46.40, 8.00], [18.02, -49.14, 8.00], [6.36, -51.20, 8.00], + [-5.98, -51.20, 8.00], +], dtype=np.float32) + +# UI constants +UI_BORDER_SIZE = 30 +BTN_SIZE = 192 +IMG_SIZE = 144 +ARC_LENGTH = 133 +ARC_THICKNESS_DEFAULT = 6.7 +ARC_THICKNESS_EXTEND = 12.0 + +SCALES_POS = np.array([0.9, 0.4, 0.4], dtype=np.float32) +SCALES_NEG = np.array([0.7, 0.4, 0.4], dtype=np.float32) + +@dataclass +class ArcData: + """Data structure for arc rendering parameters.""" + x: float + y: float + width: float + height: float + thickness: float + +class DriverStateRenderer: + def __init__(self): + # Initial state with NumPy arrays + self.face_kpts_draw = DEFAULT_FACE_KPTS_3D.copy() + self.is_active = False + self.is_rhd = False + self.dm_fade_state = 0.0 + self.state_updated = False + self.last_rect: rl.Rectangle = rl.Rectangle(0, 0, 0, 0) + self.driver_pose_vals = np.zeros(3, dtype=np.float32) + self.driver_pose_diff = np.zeros(3, dtype=np.float32) + self.driver_pose_sins = np.zeros(3, dtype=np.float32) + self.driver_pose_coss = np.zeros(3, dtype=np.float32) + self.face_keypoints_transformed = np.zeros((DEFAULT_FACE_KPTS_3D.shape[0], 2), dtype=np.float32) + self.position_x: float = 0.0 + self.position_y: float = 0.0 + self.h_arc_data = None + self.v_arc_data = None + + # Pre-allocate drawing arrays + self.face_lines = [rl.Vector2(0, 0) for _ in range(len(DEFAULT_FACE_KPTS_3D))] + self.h_arc_lines = [rl.Vector2(0, 0) for _ in range(37)] # 37 points for horizontal arc + self.v_arc_lines = [rl.Vector2(0, 0) for _ in range(37)] # 37 points for vertical arc + + # Load the driver face icon + self.dm_img = gui_app.texture("icons/driver_face.png", IMG_SIZE, IMG_SIZE) + + # Colors + self.white_color = rl.Color(255, 255, 255, 255) + self.arc_color = rl.Color(26, 242, 66, 255) + self.engaged_color = rl.Color(26, 242, 66, 255) + self.disengaged_color = rl.Color(139, 139, 139, 255) + + def draw(self, rect, sm): + if not self._is_visible(sm): + return + + self._update_state(sm, rect) + if not self.state_updated: + return + + # Set opacity based on active state + opacity = 0.65 if self.is_active else 0.2 + + # Draw background circle + rl.draw_circle(int(self.position_x), int(self.position_y), BTN_SIZE // 2, rl.Color(0, 0, 0, 70)) + + # Draw face icon + icon_pos = rl.Vector2(self.position_x - self.dm_img.width // 2, self.position_y - self.dm_img.height // 2) + rl.draw_texture_v(self.dm_img, icon_pos, rl.Color(255, 255, 255, int(255 * opacity))) + + # Draw face outline + self.white_color.a = int(255 * opacity) + rl.draw_spline_linear(self.face_lines, len(self.face_lines), 5.2, self.white_color) + + # Set arc color based on engaged state + engaged = True + self.arc_color = self.engaged_color if engaged else self.disengaged_color + self.arc_color.a = int(0.4 * 255 * (1.0 - self.dm_fade_state)) # Fade out when inactive + + # Draw arcs + if self.h_arc_data: + rl.draw_spline_linear(self.h_arc_lines, len(self.h_arc_lines), self.h_arc_data.thickness, self.arc_color) + if self.v_arc_data: + rl.draw_spline_linear(self.v_arc_lines, len(self.v_arc_lines), self.v_arc_data.thickness, self.arc_color) + + def _is_visible(self, sm): + """Check if the visualization should be rendered.""" + return (sm.seen['driverStateV2'] and + sm.seen['driverMonitoringState'] and + sm['selfdriveState'].alertSize == 0) + + def _update_state(self, sm, rect): + """Update the driver monitoring state based on model data""" + if not sm.updated["driverMonitoringState"]: + if self.state_updated and (rect.x != self.last_rect.x or rect.y != self.last_rect.y or \ + rect.width != self.last_rect.width or rect.height != self.last_rect.height): + self.pre_calculate_drawing_elements(rect) + return + + # Get monitoring state + dm_state = sm["driverMonitoringState"] + self.is_active = dm_state.isActiveMode + self.is_rhd = dm_state.isRHD + + # Update fade state (smoother transition between active/inactive) + fade_target = 0.0 if self.is_active else 0.5 + self.dm_fade_state = np.clip(self.dm_fade_state + 0.2 * (fade_target - self.dm_fade_state), 0.0, 1.0) + + # Get driver orientation data from appropriate camera + driverstate = sm["driverStateV2"] + driver_data = driverstate.rightDriverData if self.is_rhd else driverstate.leftDriverData + driver_orient = driver_data.faceOrientation + + # Update pose values with scaling and smoothing + driver_orient = np.array(driver_orient) + scales = np.where(driver_orient < 0, SCALES_NEG, SCALES_POS) + v_this = driver_orient * scales + self.driver_pose_diff = np.abs(self.driver_pose_vals - v_this) + self.driver_pose_vals = 0.8 * v_this + 0.2 * self.driver_pose_vals # Smooth changes + + # Apply fade to rotation and compute sin/cos + rotation_amount = self.driver_pose_vals * (1.0 - self.dm_fade_state) + self.driver_pose_sins = np.sin(rotation_amount) + self.driver_pose_coss = np.cos(rotation_amount) + + # Create rotation matrix for 3D face model + sin_y, sin_x, sin_z = self.driver_pose_sins + cos_y, cos_x, cos_z = self.driver_pose_coss + r_xyz = np.array( + [ + [cos_x * cos_z, cos_x * sin_z, -sin_x], + [-sin_y * sin_x * cos_z - cos_y * sin_z, -sin_y * sin_x * sin_z + cos_y * cos_z, -sin_y * cos_x], + [cos_y * sin_x * cos_z - sin_y * sin_z, cos_y * sin_x * sin_z + sin_y * cos_z, cos_y * cos_x], + ] + ) + + # Transform face keypoints using vectorized matrix multiplication + self.face_kpts_draw = DEFAULT_FACE_KPTS_3D @ r_xyz.T + self.face_kpts_draw[:, 2] = self.face_kpts_draw[:, 2] * (1.0 - self.dm_fade_state) + 8 * self.dm_fade_state + + # Pre-calculate the transformed keypoints + kp_depth = (self.face_kpts_draw[:, 2] - 8) / 120.0 + 1.0 + self.face_keypoints_transformed = self.face_kpts_draw[:, :2] * kp_depth[:, None] + + # Pre-calculate all drawing elements + self._pre_calculate_drawing_elements(rect) + self.state_updated = True + + def _pre_calculate_drawing_elements(self, rect): + """Pre-calculate all drawing elements based on the current rectangle""" + # Calculate icon position (bottom-left or bottom-right) + width, height = rect.width, rect.height + offset = UI_BORDER_SIZE + BTN_SIZE // 2 + self.position_x = rect.x + (width - offset if self.is_rhd else offset) + self.position_y = rect.y + height - offset + + # Pre-calculate the face lines positions + positioned_keypoints = self.face_keypoints_transformed + np.array([self.position_x, self.position_y]) + for i in range(len(positioned_keypoints)): + self.face_lines[i].x = positioned_keypoints[i][0] + self.face_lines[i].y = positioned_keypoints[i][1] + + # Calculate arc dimensions based on head rotation + delta_x = -self.driver_pose_sins[1] * ARC_LENGTH / 2.0 # Horizontal movement + delta_y = -self.driver_pose_sins[0] * ARC_LENGTH / 2.0 # Vertical movement + + # Horizontal arc + h_width = abs(delta_x) + self.h_arc_data = self._calculate_arc_data( + delta_x, h_width, self.position_x, self.position_y - ARC_LENGTH / 2, + self.driver_pose_sins[1], self.driver_pose_diff[1], is_horizontal=True + ) + + # Vertical arc + v_height = abs(delta_y) + self.v_arc_data = self._calculate_arc_data( + delta_y, v_height, self.position_x - ARC_LENGTH / 2, self.position_y, + self.driver_pose_sins[0], self.driver_pose_diff[0], is_horizontal=False + ) + + def _calculate_arc_data( + self, delta: float, size: float, x: float, y: float, sin_val: float, diff_val: float, is_horizontal: bool + ): + """Calculate arc data and pre-compute arc points.""" + if size <= 0: + return None + + thickness = ARC_THICKNESS_DEFAULT + ARC_THICKNESS_EXTEND * min(1.0, diff_val * 5.0) + start_angle = (90 if sin_val > 0 else -90) if is_horizontal else (0 if sin_val > 0 else 180) + x = min(x + delta, x) if is_horizontal else x + y = y if is_horizontal else min(y + delta, y) + + arc_data = ArcData( + x=x, + y=y, + width=size if is_horizontal else ARC_LENGTH, + height=ARC_LENGTH if is_horizontal else size, + thickness=thickness, + ) + + # Pre-calculate arc points + start_rad = np.deg2rad(start_angle) + end_rad = np.deg2rad(start_angle + 180) + angles = np.linspace(start_rad, end_rad, 37) + + center_x = x + arc_data.width / 2 + center_y = y + arc_data.height / 2 + radius_x = arc_data.width / 2 + radius_y = arc_data.height / 2 + + x_coords = center_x + np.cos(angles) * radius_x + y_coords = center_y + np.sin(angles) * radius_y + + arc_lines = self.h_arc_lines if is_horizontal else self.v_arc_lines + for i, (x_coord, y_coord) in enumerate(zip(x_coords, y_coords, strict=True)): + arc_lines[i].x = x_coord + arc_lines[i].y = y_coord + + return arc_data From a3fab434a47c0e308021984950d39e5e6f2d8a62 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 00:53:11 +0800 Subject: [PATCH 378/477] system/ui: migrate c++ HudRenderer to python (#35359) * port c++ HudRenderer to python * cache font metrics * cache fonts * improve * fix bg * refactor * rebase * fix --- system/ui/onroad/augmented_road_view.py | 3 + system/ui/onroad/driver_state.py | 2 +- system/ui/onroad/hud_renderer.py | 194 ++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 system/ui/onroad/hud_renderer.py diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 67f6bb99a9..7128917adf 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -5,6 +5,7 @@ from enum import Enum from cereal import messaging, log from msgq.visionipc import VisionStreamType from openpilot.system.ui.onroad.driver_state import DriverStateRenderer +from openpilot.system.ui.onroad.hud_renderer import HudRenderer from openpilot.system.ui.onroad.model_renderer import ModelRenderer from openpilot.system.ui.widgets.cameraview import CameraView from openpilot.system.ui.lib.application import gui_app @@ -41,6 +42,7 @@ class AugmentedRoadView(CameraView): self._content_rect = rl.Rectangle() self.model_renderer = ModelRenderer() + self._hud_renderer = HudRenderer() self.driver_state_renderer = DriverStateRenderer() def render(self, rect): @@ -76,6 +78,7 @@ class AugmentedRoadView(CameraView): # - Lead vehicle indicators # - Additional features self.model_renderer.draw(self._content_rect, self.sm) + self._hud_renderer.draw(self._content_rect, self.sm) self.driver_state_renderer.draw(self._content_rect, self.sm) # End clipping region diff --git a/system/ui/onroad/driver_state.py b/system/ui/onroad/driver_state.py index 4ad35bfaa5..b998936903 100644 --- a/system/ui/onroad/driver_state.py +++ b/system/ui/onroad/driver_state.py @@ -113,7 +113,7 @@ class DriverStateRenderer: if not sm.updated["driverMonitoringState"]: if self.state_updated and (rect.x != self.last_rect.x or rect.y != self.last_rect.y or \ rect.width != self.last_rect.width or rect.height != self.last_rect.height): - self.pre_calculate_drawing_elements(rect) + self._pre_calculate_drawing_elements(rect) return # Get monitoring state diff --git a/system/ui/onroad/hud_renderer.py b/system/ui/onroad/hud_renderer.py new file mode 100644 index 0000000000..b63b50f26d --- /dev/null +++ b/system/ui/onroad/hud_renderer.py @@ -0,0 +1,194 @@ +import pyray as rl +from dataclasses import dataclass +from cereal.messaging import SubMaster +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.common.conversions import Conversions as CV +from enum import IntEnum + +# Constants +SET_SPEED_NA = 255 +KM_TO_MILE = 0.621371 + + +@dataclass(frozen=True) +class UIConfig: + header_height: int = 300 + border_size: int = 30 + button_size: int = 192 + set_speed_width_metric: int = 200 + set_speed_width_imperial: int = 172 + set_speed_height: int = 204 + wheel_icon_size: int = 144 + + +@dataclass(frozen=True) +class FontSizes: + current_speed: int = 176 + speed_unit: int = 66 + max_speed: int = 40 + set_speed: int = 90 + + +@dataclass(frozen=True) +class Colors: + white: rl.Color = rl.Color(255, 255, 255, 255) + disengaged: rl.Color = rl.Color(145, 155, 149, 255) + override: rl.Color = rl.Color(145, 155, 149, 255) # Added + engaged: rl.Color = rl.Color(128, 216, 166, 255) + disengaged_bg: rl.Color = rl.Color(0, 0, 0, 153) + override_bg: rl.Color = rl.Color(145, 155, 149, 204) + engaged_bg: rl.Color = rl.Color(128, 216, 166, 204) + grey: rl.Color = rl.Color(166, 166, 166, 255) + dark_grey: rl.Color = rl.Color(114, 114, 114, 255) + black_translucent: rl.Color = rl.Color(0, 0, 0, 166) + white_translucent: rl.Color = rl.Color(255, 255, 255, 200) + border_translucent: rl.Color = rl.Color(255, 255, 255, 75) + header_gradient_start: rl.Color = rl.Color(0, 0, 0, 114) + header_gradient_end: rl.Color = rl.Color(0, 0, 0, 0) + + +UI_CONFIG = UIConfig() +FONT_SIZES = FontSizes() +COLORS = Colors() + + +class HudStatus(IntEnum): + DISENGAGED = 0 + OVERRIDE = 1 + ENGAGED = 2 + + +class HudRenderer: + def __init__(self): + """Initialize the HUD renderer.""" + self.is_metric: bool = False + self.status: HudStatus = HudStatus.DISENGAGED + self.is_cruise_set: bool = False + self.is_cruise_available: bool = False + self.set_speed: float = SET_SPEED_NA + self.speed: float = 0.0 + self.v_ego_cluster_seen: bool = False + self.font_metrics_cache: dict[[str, int, str], rl.Vector2] = {} + self._wheel_texture: rl.Texture = gui_app.texture('icons/chffr_wheel.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size) + self._font_semi_bold: rl.Font = gui_app.font(FontWeight.SEMI_BOLD) + self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD) + self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM) + + def _update_state(self, sm: SubMaster) -> None: + """Update HUD state based on car state and controls state.""" + self.is_metric = True + self.status = HudStatus.DISENGAGED + + if not sm.valid['carState']: + self.is_cruise_set = False + self.set_speed = SET_SPEED_NA + self.speed = 0.0 + return + + controls_state = sm['controlsState'] + car_state = sm['carState'] + + v_cruise_cluster = car_state.vCruiseCluster + self.set_speed = ( + controls_state.vCruiseDEPRECATED if v_cruise_cluster == 0.0 else v_cruise_cluster + ) + self.is_cruise_set = 0 < self.set_speed < SET_SPEED_NA + self.is_cruise_available = self.set_speed != -1 + + if self.is_cruise_set and not self.is_metric: + self.set_speed *= KM_TO_MILE + + v_ego_cluster = car_state.vEgoCluster + self.v_ego_cluster_seen = self.v_ego_cluster_seen or v_ego_cluster != 0.0 + v_ego = v_ego_cluster if self.v_ego_cluster_seen else car_state.vEgo + speed_conversion = CV.MS_TO_KPH if self.is_metric else CV.MS_TO_MPH + self.speed = max(0.0, v_ego * speed_conversion) + + def draw(self, rect: rl.Rectangle, sm: SubMaster) -> None: + """Render HUD elements to the screen.""" + self._update_state(sm) + rl.draw_rectangle_gradient_v( + int(rect.x), + int(rect.y), + int(rect.width), + UI_CONFIG.header_height, + COLORS.header_gradient_start, + COLORS.header_gradient_end, + ) + + if self.is_cruise_available: + self._draw_set_speed(rect) + + self._draw_current_speed(rect) + self._draw_wheel_icon(rect) + + def _draw_set_speed(self, rect: rl.Rectangle) -> None: + """Draw the MAX speed indicator box.""" + set_speed_width = UI_CONFIG.set_speed_width_metric if self.is_metric else UI_CONFIG.set_speed_width_imperial + x = rect.x + 60 + (UI_CONFIG.set_speed_width_imperial - set_speed_width) // 2 + y = rect.y + 45 + + set_speed_rect = rl.Rectangle(x, y, set_speed_width, UI_CONFIG.set_speed_height) + rl.draw_rectangle_rounded(set_speed_rect, 0.2, 30, COLORS.black_translucent) + rl.draw_rectangle_rounded_lines_ex(set_speed_rect, 0.2, 30, 6, COLORS.border_translucent) + + max_color = COLORS.grey + set_speed_color = COLORS.dark_grey + if self.is_cruise_set: + set_speed_color = COLORS.white + max_color = { + HudStatus.DISENGAGED: COLORS.disengaged, + HudStatus.OVERRIDE: COLORS.override, + HudStatus.ENGAGED: COLORS.engaged, + }.get(self.status, COLORS.grey) + + max_text = "MAX" + max_text_width = self._measure_text(max_text, self._font_semi_bold, FONT_SIZES.max_speed, 'semi_bold').x + rl.draw_text_ex( + self._font_semi_bold, + max_text, + rl.Vector2(x + (set_speed_width - max_text_width) / 2, y + 27), + FONT_SIZES.max_speed, + 0, + max_color, + ) + + set_speed_text = "–" if not self.is_cruise_set else str(round(self.set_speed)) + speed_text_width = self._measure_text(set_speed_text, self._font_bold, FONT_SIZES.set_speed, 'bold').x + rl.draw_text_ex( + self._font_bold, + set_speed_text, + rl.Vector2(x + (set_speed_width - speed_text_width) / 2, y + 77), + FONT_SIZES.set_speed, + 0, + set_speed_color, + ) + + def _draw_current_speed(self, rect: rl.Rectangle) -> None: + """Draw the current vehicle speed and unit.""" + speed_text = str(round(self.speed)) + speed_text_size = self._measure_text(speed_text, self._font_bold, FONT_SIZES.current_speed, 'bold') + speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2) + rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.white) + + unit_text = "km/h" if self.is_metric else "mph" + unit_text_size = self._measure_text(unit_text, self._font_medium, FONT_SIZES.speed_unit, 'medium') + unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) + rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent) + + def _draw_wheel_icon(self, rect: rl.Rectangle) -> None: + """Draw the steering wheel icon with status-based opacity.""" + center_x = int(rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size / 2) + center_y = int(rect.y + UI_CONFIG.border_size + UI_CONFIG.button_size / 2) + rl.draw_circle(center_x, center_y, UI_CONFIG.button_size / 2, COLORS.black_translucent) + + opacity = 0.7 if self.status == HudStatus.DISENGAGED else 1.0 + img_pos = rl.Vector2(center_x - self._wheel_texture.width / 2, center_y - self._wheel_texture.height / 2) + rl.draw_texture_v(self._wheel_texture, img_pos, rl.Color(255, 255, 255, int(255 * opacity))) + + def _measure_text(self, text: str, font: rl.Font, font_size: int, font_type: str) -> rl.Vector2: + """Measure text dimensions with caching.""" + key = (text, font_size, font_type) + if key not in self.font_metrics_cache: + self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) + return self.font_metrics_cache[key] From e51243f2cd6c6e2d236b2295f336b763ff33f094 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 01:53:39 +0800 Subject: [PATCH 379/477] system/ui: remove todo and add comment (#35390) remove todo and add document --- system/ui/onroad/augmented_road_view.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 7128917adf..83d2271028 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -72,15 +72,14 @@ class AugmentedRoadView(CameraView): # Render the base camera view super().render(rect) - # TODO: Add road visualization overlays like: - # - Lane lines and road edges - # - Path prediction - # - Lead vehicle indicators - # - Additional features + # Draw all UI overlays self.model_renderer.draw(self._content_rect, self.sm) self._hud_renderer.draw(self._content_rect, self.sm) self.driver_state_renderer.draw(self._content_rect, self.sm) + # Custom UI extension point - add custom overlays here + # Use self._content_rect for positioning within camera bounds + # End clipping region rl.end_scissor_mode() From 255b606fe49fac4b3e5e83982628e67161d715b4 Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Fri, 30 May 2025 10:54:07 -0700 Subject: [PATCH 380/477] feat: remove esim.nmconnection, use AGNOS lte conn (#35389) * feat: remove esim.nmconnection, use AGNOS lte conn * unused * remove old --- system/hardware/base.py | 3 +++ system/hardware/tici/esim.nmconnection | 30 -------------------------- system/hardware/tici/hardware.py | 22 +++++++++---------- 3 files changed, 14 insertions(+), 41 deletions(-) delete mode 100644 system/hardware/tici/esim.nmconnection diff --git a/system/hardware/base.py b/system/hardware/base.py index e429f0e9f2..3506be5145 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -210,6 +210,9 @@ class HardwareBase(ABC): def configure_modem(self): pass + def reboot_modem(self): + pass + @abstractmethod def get_networks(self): pass diff --git a/system/hardware/tici/esim.nmconnection b/system/hardware/tici/esim.nmconnection deleted file mode 100644 index 74f6f8e82c..0000000000 --- a/system/hardware/tici/esim.nmconnection +++ /dev/null @@ -1,30 +0,0 @@ -[connection] -id=esim -uuid=fff6553c-3284-4707-a6b1-acc021caaafb -type=gsm -permissions= -autoconnect=true -autoconnect-retries=100 -autoconnect-priority=2 -metered=1 - -[gsm] -apn= -home-only=false -auto-config=true -sim-id= - -[ipv4] -route-metric=1000 -dns-priority=1000 -dns-search= -method=auto - -[ipv6] -ddr-gen-mode=stable-privacy -dns-search= -route-metric=1000 -dns-priority=1000 -method=auto - -[proxy] diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 3add52d21c..f5991ec7fa 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -3,7 +3,6 @@ import math import os import subprocess import time -import tempfile from enum import IntEnum from functools import cached_property, lru_cache from pathlib import Path @@ -518,18 +517,19 @@ class Tici(HardwareBase): except Exception: pass - # eSIM prime + # we use the lte connection built into AGNOS. cleanup esim connection if it exists dest = "/etc/NetworkManager/system-connections/esim.nmconnection" - if sim_id.startswith('8985235') and not os.path.exists(dest): - with open(Path(__file__).parent/'esim.nmconnection') as f, tempfile.NamedTemporaryFile(mode='w') as tf: - dat = f.read() - dat = dat.replace("sim-id=", f"sim-id={sim_id}") - tf.write(dat) - tf.flush() + if os.path.exists(dest): + os.system(f"sudo nmcli con delete {dest}") + self.reboot_modem() - # needs to be root - os.system(f"sudo cp {tf.name} {dest}") - os.system(f"sudo nmcli con load {dest}") + def reboot_modem(self): + modem = self.get_modem() + for state in (0, 1): + try: + modem.Command(f'AT+CFUN={state}', math.ceil(TIMEOUT), dbus_interface=MM_MODEM, timeout=TIMEOUT) + except Exception: + pass def get_networks(self): r = {} From 122182176161bff56e51b68afcfec07264925093 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 02:03:38 +0800 Subject: [PATCH 381/477] system/ui: migrate c++ alert renderer to python (#35386) * rebase * cache metrics * measure text * type hint * improve * fix roundness * rebase --- system/ui/onroad/alert_renderer.py | 234 ++++++++++++++++++++++++ system/ui/onroad/augmented_road_view.py | 5 +- 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 system/ui/onroad/alert_renderer.py diff --git a/system/ui/onroad/alert_renderer.py b/system/ui/onroad/alert_renderer.py new file mode 100644 index 0000000000..600c721680 --- /dev/null +++ b/system/ui/onroad/alert_renderer.py @@ -0,0 +1,234 @@ +import numpy as np +import pyray as rl +from dataclasses import dataclass +from cereal import messaging, log +from openpilot.system.ui.lib.application import gui_app, FontWeight + +# Constants +ALERT_COLORS = { + log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 150), # Black + log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 100), # Orange + log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 150), # Red +} + +ALERT_HEIGHTS = { + log.SelfdriveState.AlertSize.small: 271, + log.SelfdriveState.AlertSize.mid: 420, +} + +SELFDRIVE_STATE_TIMEOUT = 5 # Seconds +SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds + + +@dataclass +class Alert: + text1: str = "" + text2: str = "" + alert_type: str = "" + size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none + status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal + + def is_equal(self, other: 'Alert') -> bool: + """Check if two alerts are equal.""" + return ( + self.text1 == other.text1 + and self.text2 == other.text2 + and self.alert_type == other.alert_type + and self.size == other.size + and self.status == other.status + ) + + +class AlertRenderer: + def __init__(self): + """Initialize the alert renderer.""" + self.alert: Alert = Alert() + self.started_frame: int = 0 + self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) + self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) + self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {} + + def clear(self) -> None: + """Reset the alert to its default state.""" + self.alert = Alert() + + def update_state(self, sm: messaging.SubMaster, started_frame: int) -> None: + """Update alert state based on SubMaster data.""" + self.started_frame = started_frame + new_alert = self.get_alert(sm) + if not self.alert.is_equal(new_alert): + self.alert = new_alert + + def get_alert(self, sm: messaging.SubMaster) -> Alert: + """Generate the current alert based on selfdrive state.""" + if not sm.valid['selfdriveState']: + return Alert() + + ss = sm['selfdriveState'] + selfdrive_frame = sm.recv_frame['selfdriveState'] + alert_status = self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus) + + # Return current alert if selfdrive state is recent + if selfdrive_frame >= self.started_frame: + return Alert( + text1=ss.alertText1, + text2=ss.alertText2, + alert_type=ss.alertType, + size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize), + status=alert_status, + ) + + # Handle selfdrive timeout + ss_missing = (np.uint64(rl.get_time() * 1e9) - sm.recv_time['selfdriveState']) / 1e9 + if selfdrive_frame < self.started_frame: + return Alert( + text1="openpilot Unavailable", + text2="Waiting to start", + alert_type="selfdriveWaiting", + size=log.SelfdriveState.AlertSize.mid, + status=log.SelfdriveState.AlertStatus.normal, + ) + elif ss_missing > SELFDRIVE_STATE_TIMEOUT: + if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT: + return Alert( + text1="TAKE CONTROL IMMEDIATELY", + text2="System Unresponsive", + alert_type="selfdriveUnresponsive", + size=log.SelfdriveState.AlertSize.full, + status=log.SelfdriveState.AlertStatus.critical, + ) + return Alert( + text1="System Unresponsive", + text2="Reboot Device", + alert_type="selfdriveUnresponsivePermanent", + size=log.SelfdriveState.AlertSize.mid, + status=log.SelfdriveState.AlertStatus.normal, + ) + + return Alert() + + def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None: + """Render the alert within the specified rectangle.""" + self.update_state(sm, sm.recv_frame['selfdriveState']) + alert_size = self._get_enum_value(self.alert.size, log.SelfdriveState.AlertSize) + if alert_size == log.SelfdriveState.AlertSize.none: + return + + # Calculate alert rectangle + margin = 0 if alert_size == log.SelfdriveState.AlertSize.full else 40 + radius = 0 if alert_size == log.SelfdriveState.AlertSize.full else 30 + height = ALERT_HEIGHTS.get(alert_size, rect.height) + alert_rect = rl.Rectangle( + rect.x + margin, + rect.y + rect.height - height + margin, + rect.width - margin * 2, + height - margin * 2, + ) + + # Draw background + alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus) + color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal]) + if alert_size != log.SelfdriveState.AlertSize.full: + roundness = radius / (min(alert_rect.width, alert_rect.height) / 2) + rl.draw_rectangle_rounded(alert_rect, roundness, 10, color) + else: + rl.draw_rectangle_rec(alert_rect, color) + + # Draw text + center_x = rect.x + rect.width / 2 + center_y = alert_rect.y + alert_rect.height / 2 + self._draw_text(alert_size, alert_rect, center_x, center_y) + + def _draw_text( + self, alert_size: log.SelfdriveState.AlertSize, alert_rect: rl.Rectangle, center_x: float, center_y: float + ) -> None: + """Draw text based on alert size.""" + if alert_size == log.SelfdriveState.AlertSize.small: + font_size = 74 + text_width = self._measure_text(self.font_bold, self.alert.text1, font_size, 'bold').x + rl.draw_text_ex( + self.font_bold, + self.alert.text1, + rl.Vector2(center_x - text_width / 2, center_y - font_size / 2), + font_size, + 0, + rl.WHITE, + ) + elif alert_size == log.SelfdriveState.AlertSize.mid: + font_size1 = 88 + text1_width = self._measure_text(self.font_bold, self.alert.text1, font_size1, 'bold').x + rl.draw_text_ex( + self.font_bold, + self.alert.text1, + rl.Vector2(center_x - text1_width / 2, center_y - 125), + font_size1, + 0, + rl.WHITE, + ) + font_size2 = 66 + text2_width = self._measure_text(self.font_regular, self.alert.text2, font_size2, 'regular').x + rl.draw_text_ex( + self.font_regular, + self.alert.text2, + rl.Vector2(center_x - text2_width / 2, center_y + 21), + font_size2, + 0, + rl.WHITE, + ) + elif alert_size == log.SelfdriveState.AlertSize.full: + is_long = len(self.alert.text1) > 15 + font_size1 = 132 if is_long else 177 + text1_y = alert_rect.y + (240 if is_long else 270) + wrapped_text1 = self._wrap_text(self.alert.text1, alert_rect.width - 100, font_size1, self.font_bold) + for i, line in enumerate(wrapped_text1): + line_width = self._measure_text(self.font_bold, line, font_size1, 'bold').x + rl.draw_text_ex( + self.font_bold, + line, + rl.Vector2(center_x - line_width / 2, text1_y + i * font_size1), + font_size1, + 0, + rl.WHITE, + ) + font_size2 = 88 + text2_y = alert_rect.y + alert_rect.height - (361 if is_long else 420) + wrapped_text2 = self._wrap_text(self.alert.text2, alert_rect.width - 100, font_size2, self.font_regular) + for i, line in enumerate(wrapped_text2): + line_width = self._measure_text(self.font_regular, line, font_size2, 'regular').x + rl.draw_text_ex( + self.font_regular, + line, + rl.Vector2(center_x - line_width / 2, text2_y + i * font_size2), + font_size2, + 0, + rl.WHITE, + ) + + def _wrap_text(self, text: str, max_width: float, font_size: int, font: rl.Font) -> list[str]: + """Wrap text to fit within max width.""" + words = text.split() + lines = [] + current_line = "" + for word in words: + test_line = f"{current_line} {word}" if current_line else word + if self._measure_text(font, test_line, font_size, 'bold' if font == self.font_bold else 'regular').x <= max_width: + current_line = test_line + else: + if current_line: + lines.append(current_line) + current_line = word + if current_line: + lines.append(current_line) + return lines + + def _measure_text(self, font: rl.Font, text: str, font_size: int, font_type: str) -> rl.Vector2: + """Measure text dimensions with caching.""" + key = (text, font_size, font_type) + if key not in self.font_metrics_cache: + self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) + return self.font_metrics_cache[key] + + @staticmethod + def _get_enum_value(enum_value, enum_type: type): + """Safely convert capnp enum to Python enum value.""" + return enum_value.raw if hasattr(enum_value, 'raw') else enum_value diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 83d2271028..18f94325f4 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -4,6 +4,7 @@ from enum import Enum from cereal import messaging, log from msgq.visionipc import VisionStreamType +from openpilot.system.ui.onroad.alert_renderer import AlertRenderer from openpilot.system.ui.onroad.driver_state import DriverStateRenderer from openpilot.system.ui.onroad.hud_renderer import HudRenderer from openpilot.system.ui.onroad.model_renderer import ModelRenderer @@ -43,6 +44,7 @@ class AugmentedRoadView(CameraView): self.model_renderer = ModelRenderer() self._hud_renderer = HudRenderer() + self.alert_renderer = AlertRenderer() self.driver_state_renderer = DriverStateRenderer() def render(self, rect): @@ -75,6 +77,7 @@ class AugmentedRoadView(CameraView): # Draw all UI overlays self.model_renderer.draw(self._content_rect, self.sm) self._hud_renderer.draw(self._content_rect, self.sm) + self.alert_renderer.draw(self._content_rect, self.sm) self.driver_state_renderer.draw(self._content_rect, self.sm) # Custom UI extension point - add custom overlays here @@ -118,7 +121,7 @@ class AugmentedRoadView(CameraView): def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: # Check if we can use cached matrix - calib_time = self.sm.recv_frame.get('liveCalibration', 0) + calib_time = self.sm.recv_frame['liveCalibration'] current_dims = (self._content_rect.width, self._content_rect.height) if (self._last_calib_time == calib_time and self._last_rect_dims == current_dims and From c4f2cf52998384cc1a578cd121f2a475cbf6bb2e Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Fri, 30 May 2025 11:29:17 -0700 Subject: [PATCH 382/477] feat(esim): enable eSIM profile hotswapping (#35324) * reboot * no sleep * test * back * wait for sim * simpler * retry * Revert "retry" This reverts commit f1297160f3c085f43fc0356abb51fb52fa93ea2c. --- system/hardware/esim.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/system/hardware/esim.py b/system/hardware/esim.py index 6668a1cdd3..58ead6593f 100755 --- a/system/hardware/esim.py +++ b/system/hardware/esim.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import argparse - +import time from openpilot.system.hardware import HARDWARE @@ -14,14 +14,16 @@ if __name__ == '__main__': parser.add_argument('--nickname', nargs=2, metavar=('iccid', 'name'), help='update the nickname for a profile') args = parser.parse_args() + mutated = False lpa = HARDWARE.get_sim_lpa() if args.switch: lpa.switch_profile(args.switch) + mutated = True elif args.delete: confirm = input('are you sure you want to delete this profile? (y/N) ') if confirm == 'y': lpa.delete_profile(args.delete) - print('deleted profile, please restart device to apply changes') + mutated = True else: print('cancelled') exit(0) @@ -32,6 +34,11 @@ if __name__ == '__main__': else: parser.print_help() + if mutated: + HARDWARE.reboot_modem() + # eUICC needs a small delay post-reboot before querying profiles + time.sleep(.5) + profiles = lpa.list_profiles() print(f'\n{len(profiles)} profile{"s" if len(profiles) > 1 else ""}:') for p in profiles: From ea9ff45ccb0461c08b084bd999576cb9c990a678 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 02:31:03 +0800 Subject: [PATCH 383/477] system/ui: fix indentation (#35391) fix indentation --- system/ui/onroad/model_renderer.py | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index faf538c58d..ef7e567cda 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -331,12 +331,12 @@ class ModelRenderer: def _map_line_to_polygon(self, line: np.ndarray, y_off: float, z_off: float, max_idx: int, allow_invert: bool = True) -> np.ndarray: """Convert 3D line to 2D polygon for rendering.""" if line.shape[0] == 0: - return np.empty((0, 2), dtype=np.float32) + return np.empty((0, 2), dtype=np.float32) # Slice points and filter non-negative x-coordinates points = line[:max_idx + 1][line[:max_idx + 1, 0] >= 0] if points.shape[0] == 0: - return np.empty((0, 2), dtype=np.float32) + return np.empty((0, 2), dtype=np.float32) # Create left and right 3D points in one array n_points = points.shape[0] @@ -350,7 +350,7 @@ class ModelRenderer: proj = self._car_space_transform @ points_3d.T valid_z = np.abs(proj[2]) > 1e-6 if not np.any(valid_z): - return np.empty((0, 2), dtype=np.float32) + return np.empty((0, 2), dtype=np.float32) # Compute screen coordinates screen = proj[:2, valid_z] / proj[2, valid_z][None, :] @@ -360,29 +360,29 @@ class ModelRenderer: # Ensure consistent shapes by re-aligning valid points valid_points = np.minimum(left_screen.shape[0], right_screen.shape[0]) if valid_points == 0: - return np.empty((0, 2), dtype=np.float32) + return np.empty((0, 2), dtype=np.float32) left_screen = left_screen[:valid_points] right_screen = right_screen[:valid_points] if self._clip_region: - clip = self._clip_region - bounds_mask = ( - (left_screen[:, 0] >= clip.x) & (left_screen[:, 0] <= clip.x + clip.width) & - (left_screen[:, 1] >= clip.y) & (left_screen[:, 1] <= clip.y + clip.height) & - (right_screen[:, 0] >= clip.x) & (right_screen[:, 0] <= clip.x + clip.width) & - (right_screen[:, 1] >= clip.y) & (right_screen[:, 1] <= clip.y + clip.height) - ) - if not np.any(bounds_mask): - return np.empty((0, 2), dtype=np.float32) - left_screen = left_screen[bounds_mask] - right_screen = right_screen[bounds_mask] + clip = self._clip_region + bounds_mask = ( + (left_screen[:, 0] >= clip.x) & (left_screen[:, 0] <= clip.x + clip.width) & + (left_screen[:, 1] >= clip.y) & (left_screen[:, 1] <= clip.y + clip.height) & + (right_screen[:, 0] >= clip.x) & (right_screen[:, 0] <= clip.x + clip.width) & + (right_screen[:, 1] >= clip.y) & (right_screen[:, 1] <= clip.y + clip.height) + ) + if not np.any(bounds_mask): + return np.empty((0, 2), dtype=np.float32) + left_screen = left_screen[bounds_mask] + right_screen = right_screen[bounds_mask] if not allow_invert and left_screen.shape[0] > 1: - keep = np.concatenate(([True], np.diff(left_screen[:, 1]) < 0)) - left_screen = left_screen[keep] - right_screen = right_screen[keep] - if left_screen.shape[0] == 0: - return np.empty((0, 2), dtype=np.float32) + keep = np.concatenate(([True], np.diff(left_screen[:, 1]) < 0)) + left_screen = left_screen[keep] + right_screen = right_screen[keep] + if left_screen.shape[0] == 0: + return np.empty((0, 2), dtype=np.float32) return np.vstack((left_screen, right_screen[::-1])).astype(np.float32) From 45f90b1a553e37f5f3f48c5a968d24357ce84c0d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 03:57:37 +0800 Subject: [PATCH 384/477] system/ui: add animation to toggle (#35392) add animation to toogle --- system/ui/lib/toggle.py | 47 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index e72faef11c..5ae2500406 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -1,45 +1,49 @@ import pyray as rl -ON_COLOR = rl.GREEN +ON_COLOR = rl.Color(0, 255, 0, 255) OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) KNOB_COLOR = rl.WHITE +WIDTH, HEIGHT = 160, 80 BG_HEIGHT = 60 -KNOB_HEIGHT = 80 -WIDTH = 160 +ANIMATION_SPEED = 8.0 class Toggle: def __init__(self, x, y, initial_state=False): self._state = initial_state - self._rect = rl.Rectangle(x, y, WIDTH, KNOB_HEIGHT) + self._rect = rl.Rectangle(x, y, WIDTH, HEIGHT) + self._progress = 1.0 if initial_state else 0.0 + self._target = self._progress def handle_input(self): if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON): - mouse_pos = rl.get_mouse_position() - if rl.check_collision_point_rec(mouse_pos, self._rect): + if rl.check_collision_point_rec(rl.get_mouse_position(), self._rect): self._state = not self._state + self._target = 1.0 if self._state else 0.0 def get_state(self): return self._state + def update(self): + if abs(self._progress - self._target) > 0.01: + delta = rl.get_frame_time() * ANIMATION_SPEED + self._progress += delta if self._progress < self._target else -delta + self._progress = max(0.0, min(1.0, self._progress)) + def render(self): - self._draw_background() - self._draw_knob() + self. update() + # Draw background + bg_rect = rl.Rectangle(self._rect.x + 5, self._rect.y + 10, WIDTH - 10, BG_HEIGHT) + bg_color = self._blend_color(OFF_COLOR, ON_COLOR, self._progress) + rl.draw_rectangle_rounded(bg_rect, 1.0, 10, bg_color) - def _draw_background(self): - bg_rect = rl.Rectangle( - self._rect.x + 5, - self._rect.y + (KNOB_HEIGHT - BG_HEIGHT) / 2, - self._rect.width - 10, - BG_HEIGHT, - ) - rl.draw_rectangle_rounded(bg_rect, 1.0, 10, ON_COLOR if self._state else OFF_COLOR) + # Draw knob + knob_x = self._rect.x + HEIGHT / 2 + (WIDTH - HEIGHT) * self._progress + knob_y = self._rect.y + HEIGHT / 2 + rl.draw_circle(int(knob_x), int(knob_y), HEIGHT / 2, KNOB_COLOR) - def _draw_knob(self): - knob_radius = KNOB_HEIGHT / 2 - knob_x = self._rect.x + knob_radius if not self._state else self._rect.x + self._rect.width - knob_radius - knob_y = self._rect.y + knob_radius - rl.draw_circle(int(knob_x), int(knob_y), knob_radius, KNOB_COLOR) + def _blend_color(self, c1, c2, t): + return rl.Color(int(c1.r + (c2.r - c1.r) * t), int(c1.g + (c2.g - c1.g) * t), int(c1.b + (c2.b - c1.b) * t), 255) if __name__ == "__main__": @@ -50,4 +54,3 @@ if __name__ == "__main__": for _ in gui_app.render(): toggle.handle_input() toggle.render() - From 2f808546449ea1aa072a2ab7f0bf6f876fe40de8 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 30 May 2025 13:31:07 -0700 Subject: [PATCH 385/477] sensord: rewrite in Python (#35353) * py sensord * fix up mmc * temp * port over accel * lil more * kinda works * rm that * gpiochip * mostly there * lil more * lil more * irq timestamps * fix ts * fix double deg2rad * test passes * fix up mypy * rm one more * exception * lint: * read in all events * bump that * get under budget: * accel self test * gyro self-test * keep these readable * give it more cores * debug * valid * rewrite that --------- Co-authored-by: Comma Device --- SConstruct | 1 - common/SConscript | 4 - common/gpio.cc | 84 -------- common/gpio.h | 33 --- common/gpio.py | 35 ++++ common/i2c.cc | 92 --------- common/i2c.h | 19 -- common/util.py | 22 ++ selfdrive/test/test_onroad.py | 8 +- system/hardware/tici/hardware.py | 20 +- system/manager/process_config.py | 2 +- system/sensord/.gitignore | 1 - system/sensord/SConscript | 13 -- system/sensord/sensord.py | 139 +++++++++++++ system/sensord/sensors/__init__.py | 0 system/sensord/sensors/constants.h | 18 -- system/sensord/sensors/i2c_sensor.cc | 50 ----- system/sensord/sensors/i2c_sensor.h | 51 ----- system/sensord/sensors/i2c_sensor.py | 72 +++++++ system/sensord/sensors/lsm6ds3_accel.cc | 250 ----------------------- system/sensord/sensors/lsm6ds3_accel.h | 49 ----- system/sensord/sensors/lsm6ds3_accel.py | 157 ++++++++++++++ system/sensord/sensors/lsm6ds3_gyro.cc | 233 --------------------- system/sensord/sensors/lsm6ds3_gyro.h | 45 ---- system/sensord/sensors/lsm6ds3_gyro.py | 141 +++++++++++++ system/sensord/sensors/lsm6ds3_temp.cc | 37 ---- system/sensord/sensors/lsm6ds3_temp.h | 26 --- system/sensord/sensors/lsm6ds3_temp.py | 33 +++ system/sensord/sensors/mmc5603nj_magn.cc | 108 ---------- system/sensord/sensors/mmc5603nj_magn.h | 37 ---- system/sensord/sensors/mmc5603nj_magn.py | 76 +++++++ system/sensord/sensors/sensor.h | 24 --- system/sensord/sensors_qcom2.cc | 170 --------------- 33 files changed, 681 insertions(+), 1369 deletions(-) delete mode 100644 common/gpio.cc delete mode 100644 common/gpio.h delete mode 100644 common/i2c.cc delete mode 100644 common/i2c.h delete mode 100644 system/sensord/.gitignore delete mode 100644 system/sensord/SConscript create mode 100755 system/sensord/sensord.py create mode 100644 system/sensord/sensors/__init__.py delete mode 100644 system/sensord/sensors/constants.h delete mode 100644 system/sensord/sensors/i2c_sensor.cc delete mode 100644 system/sensord/sensors/i2c_sensor.h create mode 100644 system/sensord/sensors/i2c_sensor.py delete mode 100644 system/sensord/sensors/lsm6ds3_accel.cc delete mode 100644 system/sensord/sensors/lsm6ds3_accel.h create mode 100644 system/sensord/sensors/lsm6ds3_accel.py delete mode 100644 system/sensord/sensors/lsm6ds3_gyro.cc delete mode 100644 system/sensord/sensors/lsm6ds3_gyro.h create mode 100644 system/sensord/sensors/lsm6ds3_gyro.py delete mode 100644 system/sensord/sensors/lsm6ds3_temp.cc delete mode 100644 system/sensord/sensors/lsm6ds3_temp.h create mode 100644 system/sensord/sensors/lsm6ds3_temp.py delete mode 100644 system/sensord/sensors/mmc5603nj_magn.cc delete mode 100644 system/sensord/sensors/mmc5603nj_magn.h create mode 100644 system/sensord/sensors/mmc5603nj_magn.py delete mode 100644 system/sensord/sensors/sensor.h delete mode 100644 system/sensord/sensors_qcom2.cc diff --git a/SConstruct b/SConstruct index d5597a70fe..c534eedbe8 100644 --- a/SConstruct +++ b/SConstruct @@ -348,7 +348,6 @@ SConscript([ ]) if arch != "Darwin": SConscript([ - 'system/sensord/SConscript', 'system/logcatd/SConscript', ]) diff --git a/common/SConscript b/common/SConscript index 829db6eeec..3cdb6fc5a2 100644 --- a/common/SConscript +++ b/common/SConscript @@ -4,14 +4,10 @@ common_libs = [ 'params.cc', 'swaglog.cc', 'util.cc', - 'i2c.cc', 'watchdog.cc', 'ratekeeper.cc' ] -if arch != "Darwin": - common_libs.append('gpio.cc') - _common = env.Library('common', common_libs, LIBS="json11") files = [ diff --git a/common/gpio.cc b/common/gpio.cc deleted file mode 100644 index dd7ba34b6d..0000000000 --- a/common/gpio.cc +++ /dev/null @@ -1,84 +0,0 @@ -#include "common/gpio.h" - -#include - -#ifdef __APPLE__ -int gpio_init(int pin_nr, bool output) { - return 0; -} - -int gpio_set(int pin_nr, bool high) { - return 0; -} - -int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) { - return 0; -} - -#else - -#include -#include - -#include -#include -#include - -#include "common/util.h" -#include "common/swaglog.h" - -int gpio_init(int pin_nr, bool output) { - char pin_dir_path[50]; - int pin_dir_path_len = snprintf(pin_dir_path, sizeof(pin_dir_path), - "/sys/class/gpio/gpio%d/direction", pin_nr); - if (pin_dir_path_len <= 0) { - return -1; - } - const char *value = output ? "out" : "in"; - return util::write_file(pin_dir_path, (void*)value, strlen(value)); -} - -int gpio_set(int pin_nr, bool high) { - char pin_val_path[50]; - int pin_val_path_len = snprintf(pin_val_path, sizeof(pin_val_path), - "/sys/class/gpio/gpio%d/value", pin_nr); - if (pin_val_path_len <= 0) { - return -1; - } - return util::write_file(pin_val_path, (void*)(high ? "1" : "0"), 1); -} - -int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr) { - - // Assumed that all interrupt pins are unexported and rights are given to - // read from gpiochip0. - std::string gpiochip_path = "/dev/gpiochip" + std::to_string(gpiochiop_id); - int fd = open(gpiochip_path.c_str(), O_RDONLY); - if (fd < 0) { - LOGE("Error opening gpiochip0 fd"); - return -1; - } - - // Setup event - struct gpioevent_request rq; - rq.lineoffset = pin_nr; - rq.handleflags = GPIOHANDLE_REQUEST_INPUT; - - /* Requesting both edges as the data ready pulse from the lsm6ds sensor is - very short(75us) and is mostly detected as falling edge instead of rising. - So if it is detected as rising the following falling edge is skipped. */ - rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; - - strncpy(rq.consumer_label, consumer_label, std::size(rq.consumer_label) - 1); - int ret = util::safe_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq); - if (ret == -1) { - LOGE("Unable to get line event from ioctl : %s", strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return rq.fd; -} - -#endif diff --git a/common/gpio.h b/common/gpio.h deleted file mode 100644 index 89cdedd66c..0000000000 --- a/common/gpio.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -// Pin definitions -#ifdef QCOM2 - #define GPIO_HUB_RST_N 30 - #define GPIO_UBLOX_RST_N 32 - #define GPIO_UBLOX_SAFEBOOT_N 33 - #define GPIO_GNSS_PWR_EN 34 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */ - #define GPIO_STM_RST_N 124 - #define GPIO_STM_BOOT0 134 - #define GPIO_BMX_ACCEL_INT 21 - #define GPIO_BMX_GYRO_INT 23 - #define GPIO_BMX_MAGN_INT 87 - #define GPIO_LSM_INT 84 - #define GPIOCHIP_INT 0 -#else - #define GPIO_HUB_RST_N 0 - #define GPIO_UBLOX_RST_N 0 - #define GPIO_UBLOX_SAFEBOOT_N 0 - #define GPIO_GNSS_PWR_EN 0 /* SCHEMATIC LABEL: GPIO_UBLOX_PWR_EN */ - #define GPIO_STM_RST_N 0 - #define GPIO_STM_BOOT0 0 - #define GPIO_BMX_ACCEL_INT 0 - #define GPIO_BMX_GYRO_INT 0 - #define GPIO_BMX_MAGN_INT 0 - #define GPIO_LSM_INT 0 - #define GPIOCHIP_INT 0 -#endif - -int gpio_init(int pin_nr, bool output); -int gpio_set(int pin_nr, bool high); - -int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int pin_nr); diff --git a/common/gpio.py b/common/gpio.py index 66b2adf438..8f025a2daf 100644 --- a/common/gpio.py +++ b/common/gpio.py @@ -1,4 +1,6 @@ import os +import fcntl +import ctypes from functools import cache def gpio_init(pin: int, output: bool) -> None: @@ -52,3 +54,36 @@ def get_irqs_for_action(action: str) -> list[str]: if irq.isdigit() and action in get_irq_action(irq): ret.append(irq) return ret + +# *** gpiochip *** + +class gpioevent_data(ctypes.Structure): + _fields_ = [ + ("timestamp", ctypes.c_uint64), + ("id", ctypes.c_uint32), + ] + +class gpioevent_request(ctypes.Structure): + _fields_ = [ + ("lineoffset", ctypes.c_uint32), + ("handleflags", ctypes.c_uint32), + ("eventflags", ctypes.c_uint32), + ("label", ctypes.c_char * 32), + ("fd", ctypes.c_int) + ] + +def gpiochip_get_ro_value_fd(label: str, gpiochip_id: int, pin: int) -> int: + GPIOEVENT_REQUEST_BOTH_EDGES = 0x3 + GPIOHANDLE_REQUEST_INPUT = 0x1 + GPIO_GET_LINEEVENT_IOCTL = 0xc030b404 + + rq = gpioevent_request() + rq.lineoffset = pin + rq.handleflags = GPIOHANDLE_REQUEST_INPUT + rq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES + rq.label = label.encode('utf-8')[:31] + b'\0' + + fd = os.open(f"/dev/gpiochip{gpiochip_id}", os.O_RDONLY) + fcntl.ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, rq) + os.close(fd) + return int(rq.fd) diff --git a/common/i2c.cc b/common/i2c.cc deleted file mode 100644 index 3d6c79efc5..0000000000 --- a/common/i2c.cc +++ /dev/null @@ -1,92 +0,0 @@ -#include "common/i2c.h" - -#include -#include -#include - -#include -#include -#include - -#include "common/swaglog.h" -#include "common/util.h" - -#define UNUSED(x) (void)(x) - -#ifdef QCOM2 -// TODO: decide if we want to install libi2c-dev everywhere -extern "C" { - #include - #include -} - -I2CBus::I2CBus(uint8_t bus_id) { - char bus_name[20]; - snprintf(bus_name, 20, "/dev/i2c-%d", bus_id); - - i2c_fd = HANDLE_EINTR(open(bus_name, O_RDWR)); - if (i2c_fd < 0) { - throw std::runtime_error("Failed to open I2C bus"); - } -} - -I2CBus::~I2CBus() { - if (i2c_fd >= 0) { - close(i2c_fd); - } -} - -int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) { - std::lock_guard lk(m); - - int ret = 0; - - ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address)); - if (ret < 0) { goto fail; } - - ret = i2c_smbus_read_i2c_block_data(i2c_fd, register_address, len, buffer); - if ((ret < 0) || (ret != len)) { goto fail; } - -fail: - return ret; -} - -int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) { - std::lock_guard lk(m); - - int ret = 0; - - ret = HANDLE_EINTR(ioctl(i2c_fd, I2C_SLAVE, device_address)); - if (ret < 0) { goto fail; } - - ret = i2c_smbus_write_byte_data(i2c_fd, register_address, data); - if (ret < 0) { goto fail; } - -fail: - return ret; -} - -#else - -I2CBus::I2CBus(uint8_t bus_id) { - UNUSED(bus_id); - i2c_fd = -1; -} - -I2CBus::~I2CBus() {} - -int I2CBus::read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len) { - UNUSED(device_address); - UNUSED(register_address); - UNUSED(buffer); - UNUSED(len); - return -1; -} - -int I2CBus::set_register(uint8_t device_address, uint register_address, uint8_t data) { - UNUSED(device_address); - UNUSED(register_address); - UNUSED(data); - return -1; -} -#endif diff --git a/common/i2c.h b/common/i2c.h deleted file mode 100644 index ca0d4635b8..0000000000 --- a/common/i2c.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -#include - -class I2CBus { - private: - int i2c_fd; - std::mutex m; - - public: - I2CBus(uint8_t bus_id); - ~I2CBus(); - - int read_register(uint8_t device_address, uint register_address, uint8_t *buffer, uint8_t len); - int set_register(uint8_t device_address, uint register_address, uint8_t data); -}; diff --git a/common/util.py b/common/util.py index 33885a9024..e6ddb46e7b 100644 --- a/common/util.py +++ b/common/util.py @@ -1,3 +1,25 @@ +import os +import subprocess + +def sudo_write(val: str, path: str) -> None: + try: + with open(path, 'w') as f: + f.write(str(val)) + except PermissionError: + os.system(f"sudo chmod a+w {path}") + try: + with open(path, 'w') as f: + f.write(str(val)) + except PermissionError: + # fallback for debugfs files + os.system(f"sudo su -c 'echo {val} > {path}'") + +def sudo_read(path: str) -> str: + try: + return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip() + except Exception: + return "" + class MovingAverage: def __init__(self, window_size: int): self.window_size: int = window_size diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index d0c725cacb..f98e3bd88c 100644 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -40,14 +40,14 @@ PROCS = { "selfdrive.selfdrived.selfdrived": 16.0, "selfdrive.car.card": 26.0, "./loggerd": 14.0, - "./encoderd": 17.0, + "./encoderd": 13.0, "./camerad": 10.0, - "selfdrive.controls.plannerd": 9.0, + "selfdrive.controls.plannerd": 8.0, "./ui": 18.0, - "./sensord": 7.0, + "system.sensord.sensord": 13.0, "selfdrive.controls.radard": 2.0, "selfdrive.modeld.modeld": 22.0, - "selfdrive.modeld.dmonitoringmodeld": 21.0, + "selfdrive.modeld.dmonitoringmodeld": 18.0, "system.hardware.hardwared": 4.0, "selfdrive.locationd.calibrationd": 2.0, "selfdrive.locationd.torqued": 5.0, diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index f5991ec7fa..f46c94c4f0 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -8,6 +8,7 @@ from functools import cached_property, lru_cache from pathlib import Path from cereal import log +from openpilot.common.util import sudo_read, sudo_write from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action from openpilot.system.hardware.base import HardwareBase, LPABase, ThermalConfig, ThermalZone from openpilot.system.hardware.tici import iwlist @@ -61,25 +62,6 @@ MM_MODEM_ACCESS_TECHNOLOGY_UMTS = 1 << 5 MM_MODEM_ACCESS_TECHNOLOGY_LTE = 1 << 14 -def sudo_write(val, path): - try: - with open(path, 'w') as f: - f.write(str(val)) - except PermissionError: - os.system(f"sudo chmod a+w {path}") - try: - with open(path, 'w') as f: - f.write(str(val)) - except PermissionError: - # fallback for debugfs files - os.system(f"sudo su -c 'echo {val} > {path}'") - -def sudo_read(path: str) -> str: - try: - return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip() - except Exception: - return "" - def affine_irq(val, action): irqs = get_irqs_for_action(action) if len(irqs) == 0: diff --git a/system/manager/process_config.py b/system/manager/process_config.py index e25d556037..738c20a85b 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -78,7 +78,7 @@ procs = [ PythonProcess("modeld", "selfdrive.modeld.modeld", only_onroad), PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), - NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC), + PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC), NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad), diff --git a/system/sensord/.gitignore b/system/sensord/.gitignore deleted file mode 100644 index e17675e254..0000000000 --- a/system/sensord/.gitignore +++ /dev/null @@ -1 +0,0 @@ -sensord diff --git a/system/sensord/SConscript b/system/sensord/SConscript deleted file mode 100644 index 1222f0bfcd..0000000000 --- a/system/sensord/SConscript +++ /dev/null @@ -1,13 +0,0 @@ -Import('env', 'arch', 'common', 'messaging') - -sensors = [ - 'sensors/i2c_sensor.cc', - 'sensors/lsm6ds3_accel.cc', - 'sensors/lsm6ds3_gyro.cc', - 'sensors/lsm6ds3_temp.cc', - 'sensors/mmc5603nj_magn.cc', -] -libs = [common, messaging, 'pthread'] -if arch == "larch64": - libs.append('i2c') -env.Program('sensord', ['sensors_qcom2.cc'] + sensors, LIBS=libs) diff --git a/system/sensord/sensord.py b/system/sensord/sensord.py new file mode 100755 index 0000000000..ed9d0ed415 --- /dev/null +++ b/system/sensord/sensord.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +import os +import time +import ctypes +import select +import threading + +import cereal.messaging as messaging +from cereal.services import SERVICE_LIST +from openpilot.common.util import sudo_write +from openpilot.common.realtime import config_realtime_process, Ratekeeper +from openpilot.common.swaglog import cloudlog +from openpilot.common.gpio import gpiochip_get_ro_value_fd, gpioevent_data + +from openpilot.system.sensord.sensors.i2c_sensor import Sensor +from openpilot.system.sensord.sensors.lsm6ds3_accel import LSM6DS3_Accel +from openpilot.system.sensord.sensors.lsm6ds3_gyro import LSM6DS3_Gyro +from openpilot.system.sensord.sensors.lsm6ds3_temp import LSM6DS3_Temp +from openpilot.system.sensord.sensors.mmc5603nj_magn import MMC5603NJ_Magn + +I2C_BUS_IMU = 1 + +def interrupt_loop(sensors: list[tuple[Sensor, str, bool]], event) -> None: + pm = messaging.PubMaster([service for sensor, service, interrupt in sensors if interrupt]) + + # Requesting both edges as the data ready pulse from the lsm6ds sensor is + # very short (75us) and is mostly detected as falling edge instead of rising. + # So if it is detected as rising the following falling edge is skipped. + fd = gpiochip_get_ro_value_fd("sensord", 0, 84) + + # Configure IRQ affinity + irq_path = "/proc/irq/336/smp_affinity_list" + if not os.path.exists(irq_path): + irq_path = "/proc/irq/335/smp_affinity_list" + if os.path.exists(irq_path): + sudo_write('1\n', irq_path) + + offset = time.time_ns() - time.monotonic_ns() + + poller = select.poll() + poller.register(fd, select.POLLIN | select.POLLPRI) + while not event.is_set(): + events = poller.poll(100) + if not events: + cloudlog.error("poll timed out") + continue + if not (events[0][1] & (select.POLLIN | select.POLLPRI)): + cloudlog.error("no poll events set") + continue + + dat = os.read(fd, ctypes.sizeof(gpioevent_data)*16) + evd = gpioevent_data.from_buffer_copy(dat) + + cur_offset = time.time_ns() - time.monotonic_ns() + if abs(cur_offset - offset) > 10 * 1e6: # ms + cloudlog.warning(f"time jumped: {cur_offset} {offset}") + offset = cur_offset + continue + + ts = evd.timestamp - cur_offset + for sensor, service, interrupt in sensors: + if interrupt: + try: + evt = sensor.get_event(ts) + if not sensor.is_data_valid(): + continue + msg = messaging.new_message(service, valid=True) + setattr(msg, service, evt) + pm.send(service, msg) + except Sensor.DataNotReady: + pass + except Exception: + cloudlog.exception(f"Error processing {service}") + + +def polling_loop(sensor: Sensor, service: str, event: threading.Event) -> None: + pm = messaging.PubMaster([service]) + rk = Ratekeeper(SERVICE_LIST[service].frequency, print_delay_threshold=None) + while not event.is_set(): + try: + evt = sensor.get_event() + if not sensor.is_data_valid(): + continue + msg = messaging.new_message(service, valid=True) + setattr(msg, service, evt) + pm.send(service, msg) + except Exception: + cloudlog.exception(f"Error in {service} polling loop") + rk.keep_time() + +def main() -> None: + config_realtime_process([1, ], 1) + + sensors_cfg = [ + (LSM6DS3_Accel(I2C_BUS_IMU), "accelerometer", True), + (LSM6DS3_Gyro(I2C_BUS_IMU), "gyroscope", True), + (LSM6DS3_Temp(I2C_BUS_IMU), "temperatureSensor", False), + (MMC5603NJ_Magn(I2C_BUS_IMU), "magnetometer", False), + ] + + # Initialize sensors + exit_event = threading.Event() + threads = [ + threading.Thread(target=interrupt_loop, args=(sensors_cfg, exit_event), daemon=True) + ] + for sensor, service, interrupt in sensors_cfg: + try: + sensor.init() + if not interrupt: + # Start polling thread for sensors without interrupts + threads.append(threading.Thread( + target=polling_loop, + args=(sensor, service, exit_event), + daemon=True + )) + except Exception: + cloudlog.exception(f"Error initializing {service} sensor") + + try: + for t in threads: + t.start() + while any(t.is_alive() for t in threads): + time.sleep(1) + except KeyboardInterrupt: + pass + finally: + exit_event.set() + for t in threads: + if t.is_alive(): + t.join() + + for sensor, _, _ in sensors_cfg: + try: + sensor.shutdown() + except Exception: + cloudlog.exception("Error shutting down sensor") + +if __name__ == "__main__": + main() diff --git a/system/sensord/sensors/__init__.py b/system/sensord/sensors/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system/sensord/sensors/constants.h b/system/sensord/sensors/constants.h deleted file mode 100644 index c216f838a5..0000000000 --- a/system/sensord/sensors/constants.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - - -#define SENSOR_ACCELEROMETER 1 -#define SENSOR_MAGNETOMETER 2 -#define SENSOR_MAGNETOMETER_UNCALIBRATED 3 -#define SENSOR_GYRO 4 -#define SENSOR_GYRO_UNCALIBRATED 5 -#define SENSOR_LIGHT 7 - -#define SENSOR_TYPE_ACCELEROMETER 1 -#define SENSOR_TYPE_GEOMAGNETIC_FIELD 2 -#define SENSOR_TYPE_GYROSCOPE 4 -#define SENSOR_TYPE_LIGHT 5 -#define SENSOR_TYPE_AMBIENT_TEMPERATURE 13 -#define SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED 14 -#define SENSOR_TYPE_MAGNETIC_FIELD SENSOR_TYPE_GEOMAGNETIC_FIELD -#define SENSOR_TYPE_GYROSCOPE_UNCALIBRATED 16 diff --git a/system/sensord/sensors/i2c_sensor.cc b/system/sensord/sensors/i2c_sensor.cc deleted file mode 100644 index 90220f551d..0000000000 --- a/system/sensord/sensors/i2c_sensor.cc +++ /dev/null @@ -1,50 +0,0 @@ -#include "system/sensord/sensors/i2c_sensor.h" - -int16_t read_12_bit(uint8_t lsb, uint8_t msb) { - uint16_t combined = (uint16_t(msb) << 8) | uint16_t(lsb & 0xF0); - return int16_t(combined) / (1 << 4); -} - -int16_t read_16_bit(uint8_t lsb, uint8_t msb) { - uint16_t combined = (uint16_t(msb) << 8) | uint16_t(lsb); - return int16_t(combined); -} - -int32_t read_20_bit(uint8_t b2, uint8_t b1, uint8_t b0) { - uint32_t combined = (uint32_t(b0) << 16) | (uint32_t(b1) << 8) | uint32_t(b2); - return int32_t(combined) / (1 << 4); -} - -I2CSensor::I2CSensor(I2CBus *bus, int gpio_nr, bool shared_gpio) : - bus(bus), gpio_nr(gpio_nr), shared_gpio(shared_gpio) {} - -I2CSensor::~I2CSensor() { - if (gpio_fd != -1) { - close(gpio_fd); - } -} - -int I2CSensor::read_register(uint register_address, uint8_t *buffer, uint8_t len) { - return bus->read_register(get_device_address(), register_address, buffer, len); -} - -int I2CSensor::set_register(uint register_address, uint8_t data) { - return bus->set_register(get_device_address(), register_address, data); -} - -int I2CSensor::init_gpio() { - if (shared_gpio || gpio_nr == 0) { - return 0; - } - - gpio_fd = gpiochip_get_ro_value_fd("sensord", GPIOCHIP_INT, gpio_nr); - if (gpio_fd < 0) { - return -1; - } - - return 0; -} - -bool I2CSensor::has_interrupt_enabled() { - return gpio_nr != 0; -} diff --git a/system/sensord/sensors/i2c_sensor.h b/system/sensord/sensors/i2c_sensor.h deleted file mode 100644 index e6d328ce72..0000000000 --- a/system/sensord/sensors/i2c_sensor.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include -#include "cereal/gen/cpp/log.capnp.h" - -#include "common/i2c.h" -#include "common/gpio.h" - -#include "common/swaglog.h" -#include "system/sensord/sensors/constants.h" -#include "system/sensord/sensors/sensor.h" - -int16_t read_12_bit(uint8_t lsb, uint8_t msb); -int16_t read_16_bit(uint8_t lsb, uint8_t msb); -int32_t read_20_bit(uint8_t b2, uint8_t b1, uint8_t b0); - - -class I2CSensor : public Sensor { -private: - I2CBus *bus; - int gpio_nr; - bool shared_gpio; - virtual uint8_t get_device_address() = 0; - -public: - I2CSensor(I2CBus *bus, int gpio_nr = 0, bool shared_gpio = false); - ~I2CSensor(); - int read_register(uint register_address, uint8_t *buffer, uint8_t len); - int set_register(uint register_address, uint8_t data); - int init_gpio(); - bool has_interrupt_enabled(); - virtual int init() = 0; - virtual bool get_event(MessageBuilder &msg, uint64_t ts = 0) = 0; - virtual int shutdown() = 0; - - int verify_chip_id(uint8_t address, const std::vector &expected_ids) { - uint8_t chip_id = 0; - int ret = read_register(address, &chip_id, 1); - if (ret < 0) { - LOGD("Reading chip ID failed: %d", ret); - return -1; - } - for (int i = 0; i < expected_ids.size(); ++i) { - if (chip_id == expected_ids[i]) return chip_id; - } - LOGE("Chip ID wrong. Got: %d, Expected %d", chip_id, expected_ids[0]); - return -1; - } -}; diff --git a/system/sensord/sensors/i2c_sensor.py b/system/sensord/sensors/i2c_sensor.py new file mode 100644 index 0000000000..0e15a6622b --- /dev/null +++ b/system/sensord/sensors/i2c_sensor.py @@ -0,0 +1,72 @@ +import time +import smbus2 +import ctypes +from collections.abc import Iterable + +from cereal import log + +class Sensor: + class SensorException(Exception): + pass + + class DataNotReady(SensorException): + pass + + def __init__(self, bus: int) -> None: + self.bus = smbus2.SMBus(bus) + self.source = log.SensorEventData.SensorSource.velodyne # unknown + self.start_ts = 0. + + def __del__(self): + self.bus.close() + + def read(self, addr: int, length: int) -> bytes: + return bytes(self.bus.read_i2c_block_data(self.device_address, addr, length)) + + def write(self, addr: int, data: int) -> None: + self.bus.write_byte_data(self.device_address, addr, data) + + def writes(self, writes: Iterable[tuple[int, int]]) -> None: + for addr, data in writes: + self.write(addr, data) + + def verify_chip_id(self, address: int, expected_ids: list[int]) -> int: + chip_id = self.read(address, 1)[0] + assert chip_id in expected_ids + return chip_id + + # Abstract methods that must be implemented by subclasses + @property + def device_address(self) -> int: + raise NotImplementedError + + def init(self) -> None: + raise NotImplementedError + + def get_event(self, ts: int | None = None) -> log.SensorEventData: + raise NotImplementedError + + def shutdown(self) -> None: + raise NotImplementedError + + def is_data_valid(self) -> bool: + if self.start_ts == 0: + self.start_ts = time.monotonic() + + # unclear whether we need this... + return (time.monotonic() - self.start_ts) > 0.5 + + # *** helpers *** + @staticmethod + def wait(): + # a standard small sleep + time.sleep(0.005) + + @staticmethod + def parse_16bit(lsb: int, msb: int) -> int: + return ctypes.c_int16((msb << 8) | lsb).value + + @staticmethod + def parse_20bit(b2: int, b1: int, b0: int) -> int: + combined = ctypes.c_uint32((b0 << 16) | (b1 << 8) | b2).value + return ctypes.c_int32(combined).value // (1 << 4) diff --git a/system/sensord/sensors/lsm6ds3_accel.cc b/system/sensord/sensors/lsm6ds3_accel.cc deleted file mode 100644 index 03533e0657..0000000000 --- a/system/sensord/sensors/lsm6ds3_accel.cc +++ /dev/null @@ -1,250 +0,0 @@ -#include "system/sensord/sensors/lsm6ds3_accel.h" - -#include -#include -#include - -#include "common/swaglog.h" -#include "common/timing.h" -#include "common/util.h" - -LSM6DS3_Accel::LSM6DS3_Accel(I2CBus *bus, int gpio_nr, bool shared_gpio) : - I2CSensor(bus, gpio_nr, shared_gpio) {} - -void LSM6DS3_Accel::wait_for_data_ready() { - uint8_t drdy = 0; - uint8_t buffer[6]; - - do { - read_register(LSM6DS3_ACCEL_I2C_REG_STAT_REG, &drdy, sizeof(drdy)); - drdy &= LSM6DS3_ACCEL_DRDY_XLDA; - } while (drdy == 0); - - read_register(LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, buffer, sizeof(buffer)); -} - -void LSM6DS3_Accel::read_and_avg_data(float* out_buf) { - uint8_t drdy = 0; - uint8_t buffer[6]; - - float scaling = 0.061f; - if (source == cereal::SensorEventData::SensorSource::LSM6DS3TRC) { - scaling = 0.122f; - } - - for (int i = 0; i < 5; i++) { - do { - read_register(LSM6DS3_ACCEL_I2C_REG_STAT_REG, &drdy, sizeof(drdy)); - drdy &= LSM6DS3_ACCEL_DRDY_XLDA; - } while (drdy == 0); - - int len = read_register(LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - for (int j = 0; j < 3; j++) { - out_buf[j] += (float)read_16_bit(buffer[j*2], buffer[j*2+1]) * scaling; - } - } - - for (int i = 0; i < 3; i++) { - out_buf[i] /= 5.0f; - } -} - -int LSM6DS3_Accel::self_test(int test_type) { - float val_st_off[3] = {0}; - float val_st_on[3] = {0}; - float test_val[3] = {0}; - uint8_t ODR_FS_MO = LSM6DS3_ACCEL_ODR_52HZ; // full scale: +-2g, ODR: 52Hz - - // prepare sensor for self-test - - // enable block data update and automatic increment - int ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL3_C, LSM6DS3_ACCEL_IF_INC_BDU); - if (ret < 0) { - return ret; - } - - if (source == cereal::SensorEventData::SensorSource::LSM6DS3TRC) { - ODR_FS_MO = LSM6DS3_ACCEL_FS_4G | LSM6DS3_ACCEL_ODR_52HZ; - } - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, ODR_FS_MO); - if (ret < 0) { - return ret; - } - - // wait for stable output, and discard first values - util::sleep_for(100); - wait_for_data_ready(); - read_and_avg_data(val_st_off); - - // enable Self Test positive (or negative) - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL5_C, test_type); - if (ret < 0) { - return ret; - } - - // wait for stable output, and discard first values - util::sleep_for(100); - wait_for_data_ready(); - read_and_avg_data(val_st_on); - - // disable sensor - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, 0); - if (ret < 0) { - return ret; - } - - // disable self test - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL5_C, 0); - if (ret < 0) { - return ret; - } - - // calculate the mg values for self test - for (int i = 0; i < 3; i++) { - test_val[i] = fabs(val_st_on[i] - val_st_off[i]); - } - - // verify test result - for (int i = 0; i < 3; i++) { - if ((LSM6DS3_ACCEL_MIN_ST_LIMIT_mg > test_val[i]) || - (test_val[i] > LSM6DS3_ACCEL_MAX_ST_LIMIT_mg)) { - return -1; - } - } - - return ret; -} - -int LSM6DS3_Accel::init() { - uint8_t value = 0; - bool do_self_test = false; - - const char* env_lsm_selftest = std::getenv("LSM_SELF_TEST"); - if (env_lsm_selftest != nullptr && strncmp(env_lsm_selftest, "1", 1) == 0) { - do_self_test = true; - } - - int ret = verify_chip_id(LSM6DS3_ACCEL_I2C_REG_ID, {LSM6DS3_ACCEL_CHIP_ID, LSM6DS3TRC_ACCEL_CHIP_ID}); - if (ret == -1) return -1; - - if (ret == LSM6DS3TRC_ACCEL_CHIP_ID) { - source = cereal::SensorEventData::SensorSource::LSM6DS3TRC; - } - - ret = self_test(LSM6DS3_ACCEL_POSITIVE_TEST); - if (ret < 0) { - LOGE("LSM6DS3 accel positive self-test failed!"); - if (do_self_test) goto fail; - } - - ret = self_test(LSM6DS3_ACCEL_NEGATIVE_TEST); - if (ret < 0) { - LOGE("LSM6DS3 accel negative self-test failed!"); - if (do_self_test) goto fail; - } - - ret = init_gpio(); - if (ret < 0) { - goto fail; - } - - // enable continuous update, and automatic increase - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL3_C, LSM6DS3_ACCEL_IF_INC); - if (ret < 0) { - goto fail; - } - - // TODO: set scale and bandwidth. Default is +- 2G, 50 Hz - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, LSM6DS3_ACCEL_ODR_104HZ); - if (ret < 0) { - goto fail; - } - - ret = set_register(LSM6DS3_ACCEL_I2C_REG_DRDY_CFG, LSM6DS3_ACCEL_DRDY_PULSE_MODE); - if (ret < 0) { - goto fail; - } - - // enable data ready interrupt for accel on INT1 - // (without resetting existing interrupts) - ret = read_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, &value, 1); - if (ret < 0) { - goto fail; - } - - value |= LSM6DS3_ACCEL_INT1_DRDY_XL; - ret = set_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, value); - -fail: - return ret; -} - -int LSM6DS3_Accel::shutdown() { - int ret = 0; - - // disable data ready interrupt for accel on INT1 - uint8_t value = 0; - ret = read_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, &value, 1); - if (ret < 0) { - goto fail; - } - - value &= ~(LSM6DS3_ACCEL_INT1_DRDY_XL); - ret = set_register(LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, value); - if (ret < 0) { - LOGE("Could not disable lsm6ds3 acceleration interrupt!"); - goto fail; - } - - // enable power-down mode - value = 0; - ret = read_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, &value, 1); - if (ret < 0) { - goto fail; - } - - value &= 0x0F; - ret = set_register(LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, value); - if (ret < 0) { - LOGE("Could not power-down lsm6ds3 accelerometer!"); - goto fail; - } - -fail: - return ret; -} - -bool LSM6DS3_Accel::get_event(MessageBuilder &msg, uint64_t ts) { - - // INT1 shared with gyro, check STATUS_REG who triggered - uint8_t status_reg = 0; - read_register(LSM6DS3_ACCEL_I2C_REG_STAT_REG, &status_reg, sizeof(status_reg)); - if ((status_reg & LSM6DS3_ACCEL_DRDY_XLDA) == 0) { - return false; - } - - uint8_t buffer[6]; - int len = read_register(LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - float scale = 9.81 * 2.0f / (1 << 15); - float x = read_16_bit(buffer[0], buffer[1]) * scale; - float y = read_16_bit(buffer[2], buffer[3]) * scale; - float z = read_16_bit(buffer[4], buffer[5]) * scale; - - auto event = msg.initEvent().initAccelerometer(); - event.setSource(source); - event.setVersion(1); - event.setSensor(SENSOR_ACCELEROMETER); - event.setType(SENSOR_TYPE_ACCELEROMETER); - event.setTimestamp(ts); - - float xyz[] = {y, -x, z}; - auto svec = event.initAcceleration(); - svec.setV(xyz); - svec.setStatus(true); - - return true; -} diff --git a/system/sensord/sensors/lsm6ds3_accel.h b/system/sensord/sensors/lsm6ds3_accel.h deleted file mode 100644 index 69667cb759..0000000000 --- a/system/sensord/sensors/lsm6ds3_accel.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define LSM6DS3_ACCEL_I2C_ADDR 0x6A - -// Registers of the chip -#define LSM6DS3_ACCEL_I2C_REG_DRDY_CFG 0x0B -#define LSM6DS3_ACCEL_I2C_REG_ID 0x0F -#define LSM6DS3_ACCEL_I2C_REG_INT1_CTRL 0x0D -#define LSM6DS3_ACCEL_I2C_REG_CTRL1_XL 0x10 -#define LSM6DS3_ACCEL_I2C_REG_CTRL3_C 0x12 -#define LSM6DS3_ACCEL_I2C_REG_CTRL5_C 0x14 -#define LSM6DS3_ACCEL_I2C_REG_CTR9_XL 0x18 -#define LSM6DS3_ACCEL_I2C_REG_STAT_REG 0x1E -#define LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL 0x28 - -// Constants -#define LSM6DS3_ACCEL_CHIP_ID 0x69 -#define LSM6DS3TRC_ACCEL_CHIP_ID 0x6A -#define LSM6DS3_ACCEL_FS_4G (0b10 << 2) -#define LSM6DS3_ACCEL_ODR_52HZ (0b0011 << 4) -#define LSM6DS3_ACCEL_ODR_104HZ (0b0100 << 4) -#define LSM6DS3_ACCEL_INT1_DRDY_XL 0b1 -#define LSM6DS3_ACCEL_DRDY_XLDA 0b1 -#define LSM6DS3_ACCEL_DRDY_PULSE_MODE (1 << 7) -#define LSM6DS3_ACCEL_IF_INC 0b00000100 -#define LSM6DS3_ACCEL_IF_INC_BDU 0b01000100 -#define LSM6DS3_ACCEL_XYZ_DEN 0b11100000 -#define LSM6DS3_ACCEL_POSITIVE_TEST 0b01 -#define LSM6DS3_ACCEL_NEGATIVE_TEST 0b10 -#define LSM6DS3_ACCEL_MIN_ST_LIMIT_mg 90.0f -#define LSM6DS3_ACCEL_MAX_ST_LIMIT_mg 1700.0f - -class LSM6DS3_Accel : public I2CSensor { - uint8_t get_device_address() {return LSM6DS3_ACCEL_I2C_ADDR;} - cereal::SensorEventData::SensorSource source = cereal::SensorEventData::SensorSource::LSM6DS3; - - // self test functions - int self_test(int test_type); - void wait_for_data_ready(); - void read_and_avg_data(float* val_st_off); -public: - LSM6DS3_Accel(I2CBus *bus, int gpio_nr = 0, bool shared_gpio = false); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown(); -}; diff --git a/system/sensord/sensors/lsm6ds3_accel.py b/system/sensord/sensors/lsm6ds3_accel.py new file mode 100644 index 0000000000..2d788fcbe2 --- /dev/null +++ b/system/sensord/sensors/lsm6ds3_accel.py @@ -0,0 +1,157 @@ +import os +import time + +from cereal import log +from openpilot.system.sensord.sensors.i2c_sensor import Sensor + +class LSM6DS3_Accel(Sensor): + LSM6DS3_ACCEL_I2C_REG_DRDY_CFG = 0x0B + LSM6DS3_ACCEL_I2C_REG_INT1_CTRL = 0x0D + LSM6DS3_ACCEL_I2C_REG_CTRL1_XL = 0x10 + LSM6DS3_ACCEL_I2C_REG_CTRL3_C = 0x12 + LSM6DS3_ACCEL_I2C_REG_CTRL5_C = 0x14 + LSM6DS3_ACCEL_I2C_REG_STAT_REG = 0x1E + LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL = 0x28 + + LSM6DS3_ACCEL_ODR_104HZ = (0b0100 << 4) + LSM6DS3_ACCEL_INT1_DRDY_XL = 0b1 + LSM6DS3_ACCEL_DRDY_XLDA = 0b1 + LSM6DS3_ACCEL_DRDY_PULSE_MODE = (1 << 7) + LSM6DS3_ACCEL_IF_INC = 0b00000100 + + LSM6DS3_ACCEL_ODR_52HZ = (0b0011 << 4) + LSM6DS3_ACCEL_FS_4G = (0b10 << 2) + LSM6DS3_ACCEL_IF_INC_BDU = 0b01000100 + LSM6DS3_ACCEL_POSITIVE_TEST = 0b01 + LSM6DS3_ACCEL_NEGATIVE_TEST = 0b10 + LSM6DS3_ACCEL_MIN_ST_LIMIT_mg = 90.0 + LSM6DS3_ACCEL_MAX_ST_LIMIT_mg = 1700.0 + + @property + def device_address(self) -> int: + return 0x6A + + def init(self): + chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A]) + if chip_id == 0x6A: + self.source = log.SensorEventData.SensorSource.lsm6ds3trc + else: + self.source = log.SensorEventData.SensorSource.lsm6ds3 + + # self-test + if os.getenv("LSM_SELF_TEST") == "1": + self.self_test(self.LSM6DS3_ACCEL_POSITIVE_TEST) + self.self_test(self.LSM6DS3_ACCEL_NEGATIVE_TEST) + + # actual init + int1 = self.read(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, 1)[0] + int1 |= self.LSM6DS3_ACCEL_INT1_DRDY_XL + self.writes(( + # Enable continuous update and automatic address increment + (self.LSM6DS3_ACCEL_I2C_REG_CTRL3_C, self.LSM6DS3_ACCEL_IF_INC), + # Set ODR to 104 Hz, FS to ±2g (default) + (self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, self.LSM6DS3_ACCEL_ODR_104HZ), + # Configure data ready signal to pulse mode + (self.LSM6DS3_ACCEL_I2C_REG_DRDY_CFG, self.LSM6DS3_ACCEL_DRDY_PULSE_MODE), + # Enable data ready interrupt on INT1 without resetting existing interrupts + (self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, int1), + )) + + def get_event(self, ts: int | None = None) -> log.SensorEventData: + assert ts is not None # must come from the IRQ event + + # Check if data is ready since IRQ is shared with gyro + status_reg = self.read(self.LSM6DS3_ACCEL_I2C_REG_STAT_REG, 1)[0] + if (status_reg & self.LSM6DS3_ACCEL_DRDY_XLDA) == 0: + raise self.DataNotReady + + scale = 9.81 * 2.0 / (1 << 15) + b = self.read(self.LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, 6) + x = self.parse_16bit(b[0], b[1]) * scale + y = self.parse_16bit(b[2], b[3]) * scale + z = self.parse_16bit(b[4], b[5]) * scale + + event = log.SensorEventData.new_message() + event.timestamp = ts + event.version = 1 + event.sensor = 1 # SENSOR_ACCELEROMETER + event.type = 1 # SENSOR_TYPE_ACCELEROMETER + event.source = self.source + a = event.init('acceleration') + a.v = [y, -x, z] + a.status = 1 + return event + + def shutdown(self) -> None: + # Disable data ready interrupt on INT1 + value = self.read(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, 1)[0] + value &= ~self.LSM6DS3_ACCEL_INT1_DRDY_XL + self.write(self.LSM6DS3_ACCEL_I2C_REG_INT1_CTRL, value) + + # Power down by clearing ODR bits + value = self.read(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, 1)[0] + value &= 0x0F + self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, value) + + # *** self-test stuff *** + def _wait_for_data_ready(self): + while True: + drdy = self.read(self.LSM6DS3_ACCEL_I2C_REG_STAT_REG, 1)[0] + if drdy & self.LSM6DS3_ACCEL_DRDY_XLDA: + break + + def _read_and_avg_data(self, scaling: float) -> list[float]: + out_buf = [0.0, 0.0, 0.0] + for _ in range(5): + self._wait_for_data_ready() + b = self.read(self.LSM6DS3_ACCEL_I2C_REG_OUTX_L_XL, 6) + for j in range(3): + val = self.parse_16bit(b[j*2], b[j*2+1]) * scaling + out_buf[j] += val + return [x / 5.0 for x in out_buf] + + def self_test(self, test_type: int) -> None: + # Prepare sensor for self-test + self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL3_C, self.LSM6DS3_ACCEL_IF_INC_BDU) + + # Configure ODR and full scale based on sensor type + if self.source == log.SensorEventData.SensorSource.lsm6ds3trc: + odr_fs = self.LSM6DS3_ACCEL_FS_4G | self.LSM6DS3_ACCEL_ODR_52HZ + scaling = 0.122 # mg/LSB for ±4g + else: + odr_fs = self.LSM6DS3_ACCEL_ODR_52HZ + scaling = 0.061 # mg/LSB for ±2g + self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, odr_fs) + + # Wait for stable output + time.sleep(0.1) + self._wait_for_data_ready() + val_st_off = self._read_and_avg_data(scaling) + + # Enable self-test + self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL5_C, test_type) + + # Wait for stable output + time.sleep(0.1) + self._wait_for_data_ready() + val_st_on = self._read_and_avg_data(scaling) + + # Disable sensor and self-test + self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL1_XL, 0) + self.write(self.LSM6DS3_ACCEL_I2C_REG_CTRL5_C, 0) + + # Calculate differences and check limits + test_val = [abs(on - off) for on, off in zip(val_st_on, val_st_off, strict=False)] + for val in test_val: + if val < self.LSM6DS3_ACCEL_MIN_ST_LIMIT_mg or val > self.LSM6DS3_ACCEL_MAX_ST_LIMIT_mg: + raise self.SensorException(f"Accelerometer self-test failed for test type {test_type}") + +if __name__ == "__main__": + import numpy as np + s = LSM6DS3_Accel(1) + s.init() + time.sleep(0.2) + e = s.get_event(0) + print(e) + print(np.linalg.norm(e.acceleration.v)) + s.shutdown() diff --git a/system/sensord/sensors/lsm6ds3_gyro.cc b/system/sensord/sensors/lsm6ds3_gyro.cc deleted file mode 100644 index bb560edeab..0000000000 --- a/system/sensord/sensors/lsm6ds3_gyro.cc +++ /dev/null @@ -1,233 +0,0 @@ -#include "system/sensord/sensors/lsm6ds3_gyro.h" - -#include -#include -#include - -#include "common/swaglog.h" -#include "common/timing.h" -#include "common/util.h" - -#define DEG2RAD(x) ((x) * M_PI / 180.0) - -LSM6DS3_Gyro::LSM6DS3_Gyro(I2CBus *bus, int gpio_nr, bool shared_gpio) : - I2CSensor(bus, gpio_nr, shared_gpio) {} - -void LSM6DS3_Gyro::wait_for_data_ready() { - uint8_t drdy = 0; - uint8_t buffer[6]; - - do { - read_register(LSM6DS3_GYRO_I2C_REG_STAT_REG, &drdy, sizeof(drdy)); - drdy &= LSM6DS3_GYRO_DRDY_GDA; - } while (drdy == 0); - - read_register(LSM6DS3_GYRO_I2C_REG_OUTX_L_G, buffer, sizeof(buffer)); -} - -void LSM6DS3_Gyro::read_and_avg_data(float* out_buf) { - uint8_t drdy = 0; - uint8_t buffer[6]; - - for (int i = 0; i < 5; i++) { - do { - read_register(LSM6DS3_GYRO_I2C_REG_STAT_REG, &drdy, sizeof(drdy)); - drdy &= LSM6DS3_GYRO_DRDY_GDA; - } while (drdy == 0); - - int len = read_register(LSM6DS3_GYRO_I2C_REG_OUTX_L_G, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - for (int j = 0; j < 3; j++) { - out_buf[j] += (float)read_16_bit(buffer[j*2], buffer[j*2+1]) * 70.0f; - } - } - - // calculate the mg average values - for (int i = 0; i < 3; i++) { - out_buf[i] /= 5.0f; - } -} - -int LSM6DS3_Gyro::self_test(int test_type) { - float val_st_off[3] = {0}; - float val_st_on[3] = {0}; - float test_val[3] = {0}; - - // prepare sensor for self-test - - // full scale: 2000dps, ODR: 208Hz - int ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, LSM6DS3_GYRO_ODR_208HZ | LSM6DS3_GYRO_FS_2000dps); - if (ret < 0) { - return ret; - } - - // wait for stable output, and discard first values - util::sleep_for(150); - wait_for_data_ready(); - read_and_avg_data(val_st_off); - - // enable Self Test positive (or negative) - ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL5_C, test_type); - if (ret < 0) { - return ret; - } - - // wait for stable output, and discard first values - util::sleep_for(50); - wait_for_data_ready(); - read_and_avg_data(val_st_on); - - // disable sensor - ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, 0); - if (ret < 0) { - return ret; - } - - // disable self test - ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL5_C, 0); - if (ret < 0) { - return ret; - } - - // calculate the mg values for self test - for (int i = 0; i < 3; i++) { - test_val[i] = fabs(val_st_on[i] - val_st_off[i]); - } - - // verify test result - for (int i = 0; i < 3; i++) { - if ((LSM6DS3_GYRO_MIN_ST_LIMIT_mdps > test_val[i]) || - (test_val[i] > LSM6DS3_GYRO_MAX_ST_LIMIT_mdps)) { - return -1; - } - } - - return ret; -} - -int LSM6DS3_Gyro::init() { - uint8_t value = 0; - bool do_self_test = false; - - const char* env_lsm_selftest = std::getenv("LSM_SELF_TEST"); - if (env_lsm_selftest != nullptr && strncmp(env_lsm_selftest, "1", 1) == 0) { - do_self_test = true; - } - - int ret = verify_chip_id(LSM6DS3_GYRO_I2C_REG_ID, {LSM6DS3_GYRO_CHIP_ID, LSM6DS3TRC_GYRO_CHIP_ID}); - if (ret == -1) return -1; - - if (ret == LSM6DS3TRC_GYRO_CHIP_ID) { - source = cereal::SensorEventData::SensorSource::LSM6DS3TRC; - } - - ret = init_gpio(); - if (ret < 0) { - goto fail; - } - - ret = self_test(LSM6DS3_GYRO_POSITIVE_TEST); - if (ret < 0) { - LOGE("LSM6DS3 gyro positive self-test failed!"); - if (do_self_test) goto fail; - } - - ret = self_test(LSM6DS3_GYRO_NEGATIVE_TEST); - if (ret < 0) { - LOGE("LSM6DS3 gyro negative self-test failed!"); - if (do_self_test) goto fail; - } - - // TODO: set scale. Default is +- 250 deg/s - ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, LSM6DS3_GYRO_ODR_104HZ); - if (ret < 0) { - goto fail; - } - - ret = set_register(LSM6DS3_GYRO_I2C_REG_DRDY_CFG, LSM6DS3_GYRO_DRDY_PULSE_MODE); - if (ret < 0) { - goto fail; - } - - // enable data ready interrupt for gyro on INT1 - // (without resetting existing interrupts) - ret = read_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, &value, 1); - if (ret < 0) { - goto fail; - } - - value |= LSM6DS3_GYRO_INT1_DRDY_G; - ret = set_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value); - -fail: - return ret; -} - -int LSM6DS3_Gyro::shutdown() { - int ret = 0; - - // disable data ready interrupt for gyro on INT1 - uint8_t value = 0; - ret = read_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, &value, 1); - if (ret < 0) { - goto fail; - } - - value &= ~(LSM6DS3_GYRO_INT1_DRDY_G); - ret = set_register(LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value); - if (ret < 0) { - LOGE("Could not disable lsm6ds3 gyroscope interrupt!"); - goto fail; - } - - // enable power-down mode - value = 0; - ret = read_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, &value, 1); - if (ret < 0) { - goto fail; - } - - value &= 0x0F; - ret = set_register(LSM6DS3_GYRO_I2C_REG_CTRL2_G, value); - if (ret < 0) { - LOGE("Could not power-down lsm6ds3 gyroscope!"); - goto fail; - } - -fail: - return ret; -} - -bool LSM6DS3_Gyro::get_event(MessageBuilder &msg, uint64_t ts) { - - // INT1 shared with accel, check STATUS_REG who triggered - uint8_t status_reg = 0; - read_register(LSM6DS3_GYRO_I2C_REG_STAT_REG, &status_reg, sizeof(status_reg)); - if ((status_reg & LSM6DS3_GYRO_DRDY_GDA) == 0) { - return false; - } - - uint8_t buffer[6]; - int len = read_register(LSM6DS3_GYRO_I2C_REG_OUTX_L_G, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - float scale = 8.75 / 1000.0; - float x = DEG2RAD(read_16_bit(buffer[0], buffer[1]) * scale); - float y = DEG2RAD(read_16_bit(buffer[2], buffer[3]) * scale); - float z = DEG2RAD(read_16_bit(buffer[4], buffer[5]) * scale); - - auto event = msg.initEvent().initGyroscope(); - event.setSource(source); - event.setVersion(2); - event.setSensor(SENSOR_GYRO_UNCALIBRATED); - event.setType(SENSOR_TYPE_GYROSCOPE_UNCALIBRATED); - event.setTimestamp(ts); - - float xyz[] = {y, -x, z}; - auto svec = event.initGyroUncalibrated(); - svec.setV(xyz); - svec.setStatus(true); - - return true; -} diff --git a/system/sensord/sensors/lsm6ds3_gyro.h b/system/sensord/sensors/lsm6ds3_gyro.h deleted file mode 100644 index adaae62dd2..0000000000 --- a/system/sensord/sensors/lsm6ds3_gyro.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define LSM6DS3_GYRO_I2C_ADDR 0x6A - -// Registers of the chip -#define LSM6DS3_GYRO_I2C_REG_DRDY_CFG 0x0B -#define LSM6DS3_GYRO_I2C_REG_ID 0x0F -#define LSM6DS3_GYRO_I2C_REG_INT1_CTRL 0x0D -#define LSM6DS3_GYRO_I2C_REG_CTRL2_G 0x11 -#define LSM6DS3_GYRO_I2C_REG_CTRL5_C 0x14 -#define LSM6DS3_GYRO_I2C_REG_STAT_REG 0x1E -#define LSM6DS3_GYRO_I2C_REG_OUTX_L_G 0x22 -#define LSM6DS3_GYRO_POSITIVE_TEST (0b01 << 2) -#define LSM6DS3_GYRO_NEGATIVE_TEST (0b11 << 2) - -// Constants -#define LSM6DS3_GYRO_CHIP_ID 0x69 -#define LSM6DS3TRC_GYRO_CHIP_ID 0x6A -#define LSM6DS3_GYRO_FS_2000dps (0b11 << 2) -#define LSM6DS3_GYRO_ODR_104HZ (0b0100 << 4) -#define LSM6DS3_GYRO_ODR_208HZ (0b0101 << 4) -#define LSM6DS3_GYRO_INT1_DRDY_G 0b10 -#define LSM6DS3_GYRO_DRDY_GDA 0b10 -#define LSM6DS3_GYRO_DRDY_PULSE_MODE (1 << 7) -#define LSM6DS3_GYRO_MIN_ST_LIMIT_mdps 150000.0f -#define LSM6DS3_GYRO_MAX_ST_LIMIT_mdps 700000.0f - - -class LSM6DS3_Gyro : public I2CSensor { - uint8_t get_device_address() {return LSM6DS3_GYRO_I2C_ADDR;} - cereal::SensorEventData::SensorSource source = cereal::SensorEventData::SensorSource::LSM6DS3; - - // self test functions - int self_test(int test_type); - void wait_for_data_ready(); - void read_and_avg_data(float* val_st_off); -public: - LSM6DS3_Gyro(I2CBus *bus, int gpio_nr = 0, bool shared_gpio = false); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown(); -}; diff --git a/system/sensord/sensors/lsm6ds3_gyro.py b/system/sensord/sensors/lsm6ds3_gyro.py new file mode 100644 index 0000000000..68fd267df2 --- /dev/null +++ b/system/sensord/sensors/lsm6ds3_gyro.py @@ -0,0 +1,141 @@ +import os +import math +import time + +from cereal import log +from openpilot.system.sensord.sensors.i2c_sensor import Sensor + +class LSM6DS3_Gyro(Sensor): + LSM6DS3_GYRO_I2C_REG_DRDY_CFG = 0x0B + LSM6DS3_GYRO_I2C_REG_INT1_CTRL = 0x0D + LSM6DS3_GYRO_I2C_REG_CTRL2_G = 0x11 + LSM6DS3_GYRO_I2C_REG_CTRL5_C = 0x14 + LSM6DS3_GYRO_I2C_REG_STAT_REG = 0x1E + LSM6DS3_GYRO_I2C_REG_OUTX_L_G = 0x22 + + LSM6DS3_GYRO_ODR_104HZ = (0b0100 << 4) + LSM6DS3_GYRO_INT1_DRDY_G = 0b10 + LSM6DS3_GYRO_DRDY_GDA = 0b10 + LSM6DS3_GYRO_DRDY_PULSE_MODE = (1 << 7) + + LSM6DS3_GYRO_ODR_208HZ = (0b0101 << 4) + LSM6DS3_GYRO_FS_2000dps = (0b11 << 2) + LSM6DS3_GYRO_POSITIVE_TEST = (0b01 << 2) + LSM6DS3_GYRO_NEGATIVE_TEST = (0b11 << 2) + LSM6DS3_GYRO_MIN_ST_LIMIT_mdps = 150000.0 + LSM6DS3_GYRO_MAX_ST_LIMIT_mdps = 700000.0 + + @property + def device_address(self) -> int: + return 0x6A + + def init(self): + chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A]) + if chip_id == 0x6A: + self.source = log.SensorEventData.SensorSource.lsm6ds3trc + else: + self.source = log.SensorEventData.SensorSource.lsm6ds3 + + # self-test + if "LSM_SELF_TEST" in os.environ: + self.self_test(self.LSM6DS3_GYRO_POSITIVE_TEST) + self.self_test(self.LSM6DS3_GYRO_NEGATIVE_TEST) + + # actual init + self.writes(( + # TODO: set scale. Default is +- 250 deg/s + (self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, self.LSM6DS3_GYRO_ODR_104HZ), + # Configure data ready signal to pulse mode + (self.LSM6DS3_GYRO_I2C_REG_DRDY_CFG, self.LSM6DS3_GYRO_DRDY_PULSE_MODE), + )) + value = self.read(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, 1)[0] + value |= self.LSM6DS3_GYRO_INT1_DRDY_G + self.write(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value) + + def get_event(self, ts: int | None = None) -> log.SensorEventData: + assert ts is not None # must come from the IRQ event + + # Check if gyroscope data is ready, since it's shared with accelerometer + status_reg = self.read(self.LSM6DS3_GYRO_I2C_REG_STAT_REG, 1)[0] + if not (status_reg & self.LSM6DS3_GYRO_DRDY_GDA): + raise self.DataNotReady + + b = self.read(self.LSM6DS3_GYRO_I2C_REG_OUTX_L_G, 6) + x = self.parse_16bit(b[0], b[1]) + y = self.parse_16bit(b[2], b[3]) + z = self.parse_16bit(b[4], b[5]) + scale = (8.75 / 1000.0) * (math.pi / 180.0) + xyz = [y * scale, -x * scale, z * scale] + + event = log.SensorEventData.new_message() + event.timestamp = ts + event.version = 2 + event.sensor = 5 # SENSOR_GYRO_UNCALIBRATED + event.type = 16 # SENSOR_TYPE_GYROSCOPE_UNCALIBRATED + event.source = self.source + g = event.init('gyroUncalibrated') + g.v = xyz + g.status = 1 + return event + + def shutdown(self) -> None: + # Disable data ready interrupt on INT1 + value = self.read(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, 1)[0] + value &= ~self.LSM6DS3_GYRO_INT1_DRDY_G + self.write(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value) + + # Power down by clearing ODR bits + value = self.read(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, 1)[0] + value &= 0x0F + self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, value) + + # *** self-test stuff *** + def _wait_for_data_ready(self): + while True: + drdy = self.read(self.LSM6DS3_GYRO_I2C_REG_STAT_REG, 1)[0] + if drdy & self.LSM6DS3_GYRO_DRDY_GDA: + break + + def _read_and_avg_data(self) -> list[float]: + out_buf = [0.0, 0.0, 0.0] + for _ in range(5): + self._wait_for_data_ready() + b = self.read(self.LSM6DS3_GYRO_I2C_REG_OUTX_L_G, 6) + for j in range(3): + val = self.parse_16bit(b[j*2], b[j*2+1]) * 70.0 # mdps/LSB for 2000 dps + out_buf[j] += val + return [x / 5.0 for x in out_buf] + + def self_test(self, test_type: int): + # Set ODR to 208Hz, FS to 2000dps + self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, self.LSM6DS3_GYRO_ODR_208HZ | self.LSM6DS3_GYRO_FS_2000dps) + + # Wait for stable output + time.sleep(0.15) + self._wait_for_data_ready() + val_st_off = self._read_and_avg_data() + + # Enable self-test + self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL5_C, test_type) + + # Wait for stable output + time.sleep(0.05) + self._wait_for_data_ready() + val_st_on = self._read_and_avg_data() + + # Disable sensor and self-test + self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, 0) + self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL5_C, 0) + + # Calculate differences and check limits + test_val = [abs(on - off) for on, off in zip(val_st_on, val_st_off, strict=False)] + for val in test_val: + if val < self.LSM6DS3_GYRO_MIN_ST_LIMIT_mdps or val > self.LSM6DS3_GYRO_MAX_ST_LIMIT_mdps: + raise Exception(f"Gyroscope self-test failed for test type {test_type}") + +if __name__ == "__main__": + s = LSM6DS3_Gyro(1) + s.init() + time.sleep(0.1) + print(s.get_event(0)) + s.shutdown() diff --git a/system/sensord/sensors/lsm6ds3_temp.cc b/system/sensord/sensors/lsm6ds3_temp.cc deleted file mode 100644 index f481614154..0000000000 --- a/system/sensord/sensors/lsm6ds3_temp.cc +++ /dev/null @@ -1,37 +0,0 @@ -#include "system/sensord/sensors/lsm6ds3_temp.h" - -#include - -#include "common/swaglog.h" -#include "common/timing.h" - -LSM6DS3_Temp::LSM6DS3_Temp(I2CBus *bus) : I2CSensor(bus) {} - -int LSM6DS3_Temp::init() { - int ret = verify_chip_id(LSM6DS3_TEMP_I2C_REG_ID, {LSM6DS3_TEMP_CHIP_ID, LSM6DS3TRC_TEMP_CHIP_ID}); - if (ret == -1) return -1; - - if (ret == LSM6DS3TRC_TEMP_CHIP_ID) { - source = cereal::SensorEventData::SensorSource::LSM6DS3TRC; - } - return 0; -} - -bool LSM6DS3_Temp::get_event(MessageBuilder &msg, uint64_t ts) { - uint64_t start_time = nanos_since_boot(); - uint8_t buffer[2]; - int len = read_register(LSM6DS3_TEMP_I2C_REG_OUT_TEMP_L, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - - float scale = (source == cereal::SensorEventData::SensorSource::LSM6DS3TRC) ? 256.0f : 16.0f; - float temp = 25.0f + read_16_bit(buffer[0], buffer[1]) / scale; - - auto event = msg.initEvent().initTemperatureSensor(); - event.setSource(source); - event.setVersion(1); - event.setType(SENSOR_TYPE_AMBIENT_TEMPERATURE); - event.setTimestamp(start_time); - event.setTemperature(temp); - - return true; -} diff --git a/system/sensord/sensors/lsm6ds3_temp.h b/system/sensord/sensors/lsm6ds3_temp.h deleted file mode 100644 index 1b5b621814..0000000000 --- a/system/sensord/sensors/lsm6ds3_temp.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define LSM6DS3_TEMP_I2C_ADDR 0x6A - -// Registers of the chip -#define LSM6DS3_TEMP_I2C_REG_ID 0x0F -#define LSM6DS3_TEMP_I2C_REG_OUT_TEMP_L 0x20 - -// Constants -#define LSM6DS3_TEMP_CHIP_ID 0x69 -#define LSM6DS3TRC_TEMP_CHIP_ID 0x6A - - -class LSM6DS3_Temp : public I2CSensor { - uint8_t get_device_address() {return LSM6DS3_TEMP_I2C_ADDR;} - cereal::SensorEventData::SensorSource source = cereal::SensorEventData::SensorSource::LSM6DS3; - -public: - LSM6DS3_Temp(I2CBus *bus); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown() { return 0; } -}; diff --git a/system/sensord/sensors/lsm6ds3_temp.py b/system/sensord/sensors/lsm6ds3_temp.py new file mode 100644 index 0000000000..7d7b1c2ce1 --- /dev/null +++ b/system/sensord/sensors/lsm6ds3_temp.py @@ -0,0 +1,33 @@ +import time + +from cereal import log +from openpilot.system.sensord.sensors.i2c_sensor import Sensor + +# https://content.arduino.cc/assets/st_imu_lsm6ds3_datasheet.pdf +class LSM6DS3_Temp(Sensor): + @property + def device_address(self) -> int: + return 0x6A # Default I2C address for LSM6DS3 + + def _read_temperature(self) -> float: + scale = 16.0 if log.SensorEventData.SensorSource.lsm6ds3 else 256.0 + data = self.read(0x20, 2) + return 25 + (self.parse_16bit(data[0], data[1]) / scale) + + def init(self): + chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A]) + if chip_id == 0x6A: + self.source = log.SensorEventData.SensorSource.lsm6ds3trc + else: + self.source = log.SensorEventData.SensorSource.lsm6ds3 + + def get_event(self, ts: int | None = None) -> log.SensorEventData: + event = log.SensorEventData.new_message() + event.version = 1 + event.timestamp = int(time.monotonic() * 1e9) + event.source = self.source + event.temperature = self._read_temperature() + return event + + def shutdown(self) -> None: + pass diff --git a/system/sensord/sensors/mmc5603nj_magn.cc b/system/sensord/sensors/mmc5603nj_magn.cc deleted file mode 100644 index 0e8ba967e3..0000000000 --- a/system/sensord/sensors/mmc5603nj_magn.cc +++ /dev/null @@ -1,108 +0,0 @@ -#include "system/sensord/sensors/mmc5603nj_magn.h" - -#include -#include -#include - -#include "common/swaglog.h" -#include "common/timing.h" -#include "common/util.h" - -MMC5603NJ_Magn::MMC5603NJ_Magn(I2CBus *bus) : I2CSensor(bus) {} - -int MMC5603NJ_Magn::init() { - int ret = verify_chip_id(MMC5603NJ_I2C_REG_ID, {MMC5603NJ_CHIP_ID}); - if (ret == -1) return -1; - - // Set ODR to 0 - ret = set_register(MMC5603NJ_I2C_REG_ODR, 0); - if (ret < 0) { - goto fail; - } - - // Set BW to 0b01 for 1-150 Hz operation - ret = set_register(MMC5603NJ_I2C_REG_INTERNAL_1, 0b01); - if (ret < 0) { - goto fail; - } - -fail: - return ret; -} - -int MMC5603NJ_Magn::shutdown() { - int ret = 0; - - // disable auto reset of measurements - uint8_t value = 0; - ret = read_register(MMC5603NJ_I2C_REG_INTERNAL_0, &value, 1); - if (ret < 0) { - goto fail; - } - - value &= ~(MMC5603NJ_CMM_FREQ_EN | MMC5603NJ_AUTO_SR_EN); - ret = set_register(MMC5603NJ_I2C_REG_INTERNAL_0, value); - if (ret < 0) { - goto fail; - } - - // set ODR to 0 to leave continuous mode - ret = set_register(MMC5603NJ_I2C_REG_ODR, 0); - if (ret < 0) { - goto fail; - } - return ret; - -fail: - LOGE("Could not disable mmc5603nj auto set reset"); - return ret; -} - -void MMC5603NJ_Magn::start_measurement() { - set_register(MMC5603NJ_I2C_REG_INTERNAL_0, 0b01); - util::sleep_for(5); -} - -std::vector MMC5603NJ_Magn::read_measurement() { - int len; - uint8_t buffer[9]; - len = read_register(MMC5603NJ_I2C_REG_XOUT0, buffer, sizeof(buffer)); - assert(len == sizeof(buffer)); - float scale = 1.0 / 16384.0; - float x = (read_20_bit(buffer[6], buffer[1], buffer[0]) * scale) - 32.0; - float y = (read_20_bit(buffer[7], buffer[3], buffer[2]) * scale) - 32.0; - float z = (read_20_bit(buffer[8], buffer[5], buffer[4]) * scale) - 32.0; - std::vector xyz = {x, y, z}; - return xyz; -} - -bool MMC5603NJ_Magn::get_event(MessageBuilder &msg, uint64_t ts) { - uint64_t start_time = nanos_since_boot(); - // SET - RESET cycle - set_register(MMC5603NJ_I2C_REG_INTERNAL_0, MMC5603NJ_SET); - util::sleep_for(5); - MMC5603NJ_Magn::start_measurement(); - std::vector xyz = MMC5603NJ_Magn::read_measurement(); - - set_register(MMC5603NJ_I2C_REG_INTERNAL_0, MMC5603NJ_RESET); - util::sleep_for(5); - MMC5603NJ_Magn::start_measurement(); - std::vector reset_xyz = MMC5603NJ_Magn::read_measurement(); - - auto event = msg.initEvent().initMagnetometer(); - event.setSource(cereal::SensorEventData::SensorSource::MMC5603NJ); - event.setVersion(1); - event.setSensor(SENSOR_MAGNETOMETER_UNCALIBRATED); - event.setType(SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED); - event.setTimestamp(start_time); - - float vals[] = {xyz[0], xyz[1], xyz[2], reset_xyz[0], reset_xyz[1], reset_xyz[2]}; - bool valid = true; - if (std::any_of(std::begin(vals), std::end(vals), [](float val) { return val == -32.0; })) { - valid = false; - } - auto svec = event.initMagneticUncalibrated(); - svec.setV(vals); - svec.setStatus(valid); - return true; -} diff --git a/system/sensord/sensors/mmc5603nj_magn.h b/system/sensord/sensors/mmc5603nj_magn.h deleted file mode 100644 index 9c0fbd2521..0000000000 --- a/system/sensord/sensors/mmc5603nj_magn.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "system/sensord/sensors/i2c_sensor.h" - -// Address of the chip on the bus -#define MMC5603NJ_I2C_ADDR 0x30 - -// Registers of the chip -#define MMC5603NJ_I2C_REG_XOUT0 0x00 -#define MMC5603NJ_I2C_REG_ODR 0x1A -#define MMC5603NJ_I2C_REG_INTERNAL_0 0x1B -#define MMC5603NJ_I2C_REG_INTERNAL_1 0x1C -#define MMC5603NJ_I2C_REG_INTERNAL_2 0x1D -#define MMC5603NJ_I2C_REG_ID 0x39 - -// Constants -#define MMC5603NJ_CHIP_ID 0x10 -#define MMC5603NJ_CMM_FREQ_EN (1 << 7) -#define MMC5603NJ_AUTO_SR_EN (1 << 5) -#define MMC5603NJ_CMM_EN (1 << 4) -#define MMC5603NJ_EN_PRD_SET (1 << 3) -#define MMC5603NJ_SET (1 << 3) -#define MMC5603NJ_RESET (1 << 4) - -class MMC5603NJ_Magn : public I2CSensor { -private: - uint8_t get_device_address() {return MMC5603NJ_I2C_ADDR;} - void start_measurement(); - std::vector read_measurement(); -public: - MMC5603NJ_Magn(I2CBus *bus); - int init(); - bool get_event(MessageBuilder &msg, uint64_t ts = 0); - int shutdown(); -}; diff --git a/system/sensord/sensors/mmc5603nj_magn.py b/system/sensord/sensors/mmc5603nj_magn.py new file mode 100644 index 0000000000..255e99eb3e --- /dev/null +++ b/system/sensord/sensors/mmc5603nj_magn.py @@ -0,0 +1,76 @@ +import time + +from cereal import log +from openpilot.system.sensord.sensors.i2c_sensor import Sensor + +# https://www.mouser.com/datasheet/2/821/Memsic_09102019_Datasheet_Rev.B-1635324.pdf + +# Register addresses +REG_ODR = 0x1A +REG_INTERNAL_0 = 0x1B +REG_INTERNAL_1 = 0x1C + +# Control register settings +CMM_FREQ_EN = (1 << 7) +AUTO_SR_EN = (1 << 5) +SET = (1 << 3) +RESET = (1 << 4) + +class MMC5603NJ_Magn(Sensor): + @property + def device_address(self) -> int: + return 0x30 + + def init(self): + self.verify_chip_id(0x39, [0x10, ]) + self.writes(( + (REG_ODR, 0), + + # Set BW to 0b01 for 1-150 Hz operation + (REG_INTERNAL_1, 0b01), + )) + + def _read_data(self, cycle) -> list[float]: + # start measurement + self.write(REG_INTERNAL_0, cycle) + self.wait() + + # read out XYZ + scale = 1.0 / 16384.0 + b = self.read(0x00, 9) + return [ + (self.parse_20bit(b[6], b[1], b[0]) * scale) - 32.0, + (self.parse_20bit(b[7], b[3], b[2]) * scale) - 32.0, + (self.parse_20bit(b[8], b[5], b[4]) * scale) - 32.0, + ] + + def get_event(self, ts: int | None = None) -> log.SensorEventData: + ts = time.monotonic_ns() + + # SET - RESET cycle + xyz = self._read_data(SET) + reset_xyz = self._read_data(RESET) + vals = [*xyz, *reset_xyz] + + event = log.SensorEventData.new_message() + event.timestamp = ts + event.version = 1 + event.sensor = 3 # SENSOR_MAGNETOMETER_UNCALIBRATED + event.type = 14 # SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED + event.source = log.SensorEventData.SensorSource.mmc5603nj + + m = event.init('magneticUncalibrated') + m.v = vals + m.status = int(all(int(v) != -32 for v in vals)) + + return event + + def shutdown(self) -> None: + v = self.read(REG_INTERNAL_0, 1)[0] + self.writes(( + # disable auto-reset of measurements + (REG_INTERNAL_0, (v & (~(CMM_FREQ_EN | AUTO_SR_EN)))), + + # disable continuous mode + (REG_ODR, 0), + )) diff --git a/system/sensord/sensors/sensor.h b/system/sensord/sensors/sensor.h deleted file mode 100644 index ccf998d161..0000000000 --- a/system/sensord/sensors/sensor.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "cereal/messaging/messaging.h" - -class Sensor { -public: - int gpio_fd = -1; - bool enabled = false; - uint64_t start_ts = 0; - uint64_t init_delay = 500e6; // default dealy 500ms - - virtual ~Sensor() {} - virtual int init() = 0; - virtual bool get_event(MessageBuilder &msg, uint64_t ts = 0) = 0; - virtual bool has_interrupt_enabled() = 0; - virtual int shutdown() = 0; - - virtual bool is_data_valid(uint64_t current_ts) { - if (start_ts == 0) { - start_ts = current_ts; - } - return (current_ts - start_ts) > init_delay; - } -}; diff --git a/system/sensord/sensors_qcom2.cc b/system/sensord/sensors_qcom2.cc deleted file mode 100644 index 8ff9331b39..0000000000 --- a/system/sensord/sensors_qcom2.cc +++ /dev/null @@ -1,170 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include "cereal/services.h" -#include "cereal/messaging/messaging.h" -#include "common/i2c.h" -#include "common/ratekeeper.h" -#include "common/swaglog.h" -#include "common/timing.h" -#include "common/util.h" -#include "system/sensord/sensors/constants.h" -#include "system/sensord/sensors/lsm6ds3_accel.h" -#include "system/sensord/sensors/lsm6ds3_gyro.h" -#include "system/sensord/sensors/lsm6ds3_temp.h" -#include "system/sensord/sensors/mmc5603nj_magn.h" - -#define I2C_BUS_IMU 1 - -ExitHandler do_exit; - -void interrupt_loop(std::vector> sensors) { - PubMaster pm({"gyroscope", "accelerometer"}); - - int fd = -1; - for (auto &[sensor, msg_name] : sensors) { - if (sensor->has_interrupt_enabled()) { - fd = sensor->gpio_fd; - break; - } - } - - uint64_t offset = nanos_since_epoch() - nanos_since_boot(); - struct pollfd fd_list[1] = {0}; - fd_list[0].fd = fd; - fd_list[0].events = POLLIN | POLLPRI; - - while (!do_exit) { - int err = poll(fd_list, 1, 100); - if (err == -1) { - if (errno == EINTR) { - continue; - } - return; - } else if (err == 0) { - LOGE("poll timed out"); - continue; - } - - if ((fd_list[0].revents & (POLLIN | POLLPRI)) == 0) { - LOGE("no poll events set"); - continue; - } - - // Read all events - struct gpioevent_data evdata[16]; - err = HANDLE_EINTR(read(fd, evdata, sizeof(evdata))); - if (err < 0 || err % sizeof(*evdata) != 0) { - LOGE("error reading event data %d", err); - continue; - } - - uint64_t cur_offset = nanos_since_epoch() - nanos_since_boot(); - uint64_t diff = cur_offset > offset ? cur_offset - offset : offset - cur_offset; - if (diff > 10*1e6) { // 10ms - LOGW("time jumped: %lu %lu", cur_offset, offset); - offset = cur_offset; - - // we don't have a valid timestamp since the - // time jumped, so throw out this measurement. - continue; - } - - int num_events = err / sizeof(*evdata); - uint64_t ts = evdata[num_events - 1].timestamp - cur_offset; - - for (auto &[sensor, msg_name] : sensors) { - if (!sensor->has_interrupt_enabled()) { - continue; - } - - MessageBuilder msg; - if (!sensor->get_event(msg, ts)) { - continue; - } - - if (!sensor->is_data_valid(ts)) { - continue; - } - - pm.send(msg_name.c_str(), msg); - } - } -} - -void polling_loop(Sensor *sensor, std::string msg_name) { - PubMaster pm({msg_name.c_str()}); - RateKeeper rk(msg_name, services.at(msg_name).frequency); - while (!do_exit) { - MessageBuilder msg; - if (sensor->get_event(msg) && sensor->is_data_valid(nanos_since_boot())) { - pm.send(msg_name.c_str(), msg); - } - rk.keepTime(); - } -} - -int sensor_loop(I2CBus *i2c_bus_imu) { - // Sensor init - std::vector> sensors_init = { - {new LSM6DS3_Accel(i2c_bus_imu, GPIO_LSM_INT), "accelerometer"}, - {new LSM6DS3_Gyro(i2c_bus_imu, GPIO_LSM_INT, true), "gyroscope"}, - {new LSM6DS3_Temp(i2c_bus_imu), "temperatureSensor"}, - - {new MMC5603NJ_Magn(i2c_bus_imu), "magnetometer"}, - }; - - // Initialize sensors - std::vector threads; - for (auto &[sensor, msg_name] : sensors_init) { - int err = sensor->init(); - if (err < 0) { - continue; - } - - if (!sensor->has_interrupt_enabled()) { - threads.emplace_back(polling_loop, sensor, msg_name); - } - } - - // increase interrupt quality by pinning interrupt and process to core 1 - setpriority(PRIO_PROCESS, 0, -18); - util::set_core_affinity({1}); - - // TODO: get the IRQ number from gpiochip - std::string irq_path = "/proc/irq/336/smp_affinity_list"; - if (!util::file_exists(irq_path)) { - irq_path = "/proc/irq/335/smp_affinity_list"; - } - std::system(util::string_format("sudo su -c 'echo 1 > %s'", irq_path.c_str()).c_str()); - - // thread for reading events via interrupts - threads.emplace_back(&interrupt_loop, std::ref(sensors_init)); - - // wait for all threads to finish - for (auto &t : threads) { - t.join(); - } - - for (auto &[sensor, msg_name] : sensors_init) { - sensor->shutdown(); - delete sensor; - } - return 0; -} - -int main(int argc, char *argv[]) { - try { - auto i2c_bus_imu = std::make_unique(I2C_BUS_IMU); - return sensor_loop(i2c_bus_imu.get()); - } catch (std::exception &e) { - LOGE("I2CBus init failed"); - return -1; - } -} From b4c9964217d3be17ff1df150f39fe5d695a4261c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 30 May 2025 15:44:03 -0700 Subject: [PATCH 386/477] gpu box test script --- scripts/usb.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 scripts/usb.sh diff --git a/scripts/usb.sh b/scripts/usb.sh new file mode 100755 index 0000000000..5796cfa028 --- /dev/null +++ b/scripts/usb.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# testing the GPU box + +export XDG_CACHE_HOME=/data/tinycache +mkdir -p $XDG_CACHE_HOME + +cd /data/openpilot/tinygrad_repo/examples +while true; do + AMD=1 AMD_IFACE=usb python ./beautiful_cartpole.py + sleep 1 +done From ba9a478213828709c32a695efac389afc2affa6b Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Fri, 30 May 2025 20:13:55 -0700 Subject: [PATCH 387/477] Update Python packages (#953) * Change values to allow better 2mph to 0mph stopping * Update python package --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 83d7cf4fd1..72e5c82795 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 83d7cf4fd128cc2dabb945d043e838ab404b16b6 +Subproject commit 72e5c82795caac8916c895fbc689ccbd3368b891 From 7cd6db6f1965b5b15b54e70f855a38b163f37fa1 Mon Sep 17 00:00:00 2001 From: programanichiro <99449198+programanichiro@users.noreply.github.com> Date: Sat, 31 May 2025 13:03:07 +0900 Subject: [PATCH 388/477] Multilang: update ja translation. (#35399) ja translation --- selfdrive/ui/translations/main_ja.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 85d2494dfe..169df93c33 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -84,27 +84,27 @@
Prevent large data uploads when on a metered cellular connection - + モバイルデータ回線を使用しているときは大容量データをアップロードしません default - + 標準設定 metered - + 従量制 unmetered - + 定額制 Wi-Fi Network Metered - + 従量制のWi-Fiネットワーク Prevent large data uploads when on a metered Wi-Fi connection - + 通信制限のあるWi-Fi接続では大容量データをアップロードしません
From 5e4a4ecec874be9227b8667adf61c2d2767dcdd9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 14:42:35 +0800 Subject: [PATCH 389/477] system/ui/: add face detection box and driver state icon to DriverCameraView (#35402) add face detection box and driver state icons --- system/ui/widgets/driver_camera_view.py | 56 +++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/system/ui/widgets/driver_camera_view.py b/system/ui/widgets/driver_camera_view.py index 174fac378d..56dcbf2b9e 100644 --- a/system/ui/widgets/driver_camera_view.py +++ b/system/ui/widgets/driver_camera_view.py @@ -1,18 +1,63 @@ import numpy as np import pyray as rl +from cereal import messaging from openpilot.system.ui.widgets.cameraview import CameraView from msgq.visionipc import VisionStreamType -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.label import gui_label +from openpilot.system.ui.onroad.driver_state import DriverStateRenderer class DriverCameraView(CameraView): def __init__(self, stream_type: VisionStreamType): super().__init__("camerad", stream_type) + self.driver_state_renderer = DriverStateRenderer() - def render(self, rect): + def render(self, rect, sm): super().render(rect) - # TODO: Add additional rendering logic + if not self.frame: + gui_label( + rect, + "camera starting", + font_size=100, + font_weight=FontWeight.BOLD, + alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + ) + return + + self._draw_face_detection(rect, sm) + self.driver_state_renderer.draw(rect, sm) + + def _draw_face_detection(self, rect: rl.Rectangle, sm) -> None: + driver_state = sm["driverStateV2"] + is_rhd = driver_state.wheelOnRightProb > 0.5 + driver_data = driver_state.rightDriverData if is_rhd else driver_state.leftDriverData + face_detect = driver_data.faceProb > 0.7 + if not face_detect: + return + + # Get face position and orientation + face_x, face_y = driver_data.facePosition + face_std = max(driver_data.faceOrientationStd[0], driver_data.faceOrientationStd[1]) + alpha = 0.7 + if face_std > 0.15: + alpha = max(0.7 - (face_std - 0.15) * 3.5, 0.0) + + # use approx instead of distort_points + # TODO: replace with distort_points + fbox_x = int(1080.0 - 1714.0 * face_x) + fbox_y = int(-135.0 + (504.0 + abs(face_x) * 112.0) + (1205.0 - abs(face_x) * 724.0) * face_y) + box_size = 220 + + line_color = rl.Color(255, 255, 255, int(alpha * 255)) + rl.draw_rectangle_rounded_lines_ex( + rl.Rectangle(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size), + 35.0 / box_size / 2, + 10, + 10, + line_color, + ) def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: driver_view_ratio = 2.0 @@ -38,9 +83,12 @@ class DriverCameraView(CameraView): if __name__ == "__main__": gui_app.init_window("Driver Camera View") + sm = messaging.SubMaster(["selfdriveState", "driverStateV2", "driverMonitoringState"]) + driver_camera_view = DriverCameraView(VisionStreamType.VISION_STREAM_DRIVER) try: for _ in gui_app.render(): - driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + sm.update() + driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height), sm) finally: driver_camera_view.close() From 62a7a19d27a0e787deb7e65d16bceffbb3e2e9a4 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 30 May 2025 23:43:28 -0700 Subject: [PATCH 390/477] Revert "system/ui/: add face detection box and driver state icon to DriverCameraView" (#35403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "system/ui/: add face detection box and driver state icon to DriverCam…" This reverts commit 5e4a4ecec874be9227b8667adf61c2d2767dcdd9. --- system/ui/widgets/driver_camera_view.py | 56 ++----------------------- 1 file changed, 4 insertions(+), 52 deletions(-) diff --git a/system/ui/widgets/driver_camera_view.py b/system/ui/widgets/driver_camera_view.py index 56dcbf2b9e..174fac378d 100644 --- a/system/ui/widgets/driver_camera_view.py +++ b/system/ui/widgets/driver_camera_view.py @@ -1,63 +1,18 @@ import numpy as np import pyray as rl -from cereal import messaging from openpilot.system.ui.widgets.cameraview import CameraView from msgq.visionipc import VisionStreamType -from openpilot.system.ui.lib.application import gui_app, FontWeight -from openpilot.system.ui.lib.label import gui_label -from openpilot.system.ui.onroad.driver_state import DriverStateRenderer +from openpilot.system.ui.lib.application import gui_app class DriverCameraView(CameraView): def __init__(self, stream_type: VisionStreamType): super().__init__("camerad", stream_type) - self.driver_state_renderer = DriverStateRenderer() - def render(self, rect, sm): + def render(self, rect): super().render(rect) - if not self.frame: - gui_label( - rect, - "camera starting", - font_size=100, - font_weight=FontWeight.BOLD, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, - ) - return - - self._draw_face_detection(rect, sm) - self.driver_state_renderer.draw(rect, sm) - - def _draw_face_detection(self, rect: rl.Rectangle, sm) -> None: - driver_state = sm["driverStateV2"] - is_rhd = driver_state.wheelOnRightProb > 0.5 - driver_data = driver_state.rightDriverData if is_rhd else driver_state.leftDriverData - face_detect = driver_data.faceProb > 0.7 - if not face_detect: - return - - # Get face position and orientation - face_x, face_y = driver_data.facePosition - face_std = max(driver_data.faceOrientationStd[0], driver_data.faceOrientationStd[1]) - alpha = 0.7 - if face_std > 0.15: - alpha = max(0.7 - (face_std - 0.15) * 3.5, 0.0) - - # use approx instead of distort_points - # TODO: replace with distort_points - fbox_x = int(1080.0 - 1714.0 * face_x) - fbox_y = int(-135.0 + (504.0 + abs(face_x) * 112.0) + (1205.0 - abs(face_x) * 724.0) * face_y) - box_size = 220 - - line_color = rl.Color(255, 255, 255, int(alpha * 255)) - rl.draw_rectangle_rounded_lines_ex( - rl.Rectangle(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size), - 35.0 / box_size / 2, - 10, - 10, - line_color, - ) + # TODO: Add additional rendering logic def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: driver_view_ratio = 2.0 @@ -83,12 +38,9 @@ class DriverCameraView(CameraView): if __name__ == "__main__": gui_app.init_window("Driver Camera View") - sm = messaging.SubMaster(["selfdriveState", "driverStateV2", "driverMonitoringState"]) - driver_camera_view = DriverCameraView(VisionStreamType.VISION_STREAM_DRIVER) try: for _ in gui_app.render(): - sm.update() - driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height), sm) + driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) finally: driver_camera_view.close() From 04115b6417387136c28293a8df4075c440222da2 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sat, 31 May 2025 14:44:13 +0800 Subject: [PATCH 391/477] system/ui: Increase font size to reduce edge aliasing (#35401) improve text's antialiasing --- system/ui/lib/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 37da413806..9bb56fec8d 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -202,7 +202,7 @@ class GuiApplication: for index, font_file in enumerate(font_files): with as_file(FONT_DIR.joinpath(font_file)) as fspath: - font = rl.load_font_ex(fspath.as_posix(), 120, codepoints, codepoint_count[0]) + font = rl.load_font_ex(fspath.as_posix(), 200, codepoints, codepoint_count[0]) rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) self._fonts[index] = font From d992d138bf6f4a72a7bc21442ddfb8deb2901105 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 1 Jun 2025 01:17:15 +0800 Subject: [PATCH 392/477] system/ui: enhance polygon fill with AABB clipping and fixed fill color issues (#35408) enhance polygon fill with AABB clipping and fixed fill color issues --- system/ui/lib/shader_polygon.py | 124 ++++++++++---------------------- 1 file changed, 36 insertions(+), 88 deletions(-) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index 9ecaa926bc..94190abc36 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -16,13 +16,12 @@ uniform int pointCount; uniform vec4 fillColor; uniform vec2 resolution; -uniform bool useGradient; +uniform int useGradient; uniform vec2 gradientStart; uniform vec2 gradientEnd; uniform vec4 gradientColors[15]; uniform float gradientStops[15]; uniform int gradientColorCount; -uniform vec2 visibleGradientRange; vec4 getGradientColor(vec2 pos) { vec2 gradientDir = gradientEnd - gradientStart; @@ -30,22 +29,9 @@ vec4 getGradientColor(vec2 pos) { if (gradientLength < 0.001) return gradientColors[0]; vec2 normalizedDir = gradientDir / gradientLength; - vec2 pointVec = pos - gradientStart; - float projection = dot(pointVec, normalizedDir); + float t = clamp(dot(pos - gradientStart, normalizedDir) / gradientLength, 0.0, 1.0); - float t = projection / gradientLength; - - // Gradient clipping: remap t to visible range - float visibleStart = visibleGradientRange.x; - float visibleEnd = visibleGradientRange.y; - float visibleRange = visibleEnd - visibleStart; - - // Remap t to visible range - if (visibleRange > 0.001) { - t = visibleStart + t * visibleRange; - } - - t = clamp(t, 0.0, 1.0); + if (gradientColorCount <= 1) return gradientColors[0]; for (int i = 0; i < gradientColorCount - 1; i++) { if (t >= gradientStops[i] && t <= gradientStops[i+1]) { float segmentT = (t - gradientStops[i]) / (gradientStops[i+1] - gradientStops[i]); @@ -58,16 +44,11 @@ vec4 getGradientColor(vec2 pos) { bool isPointInsidePolygon(vec2 p) { if (pointCount < 3) return false; - int crossings = 0; for (int i = 0, j = pointCount - 1; i < pointCount; j = i++) { vec2 pi = points[i]; vec2 pj = points[j]; - - // Skip degenerate edges if (distance(pi, pj) < 0.001) continue; - - // Ray-casting if (((pi.y > p.y) != (pj.y > p.y)) && (p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y + 0.001) + pi.x)) { crossings++; @@ -104,24 +85,24 @@ float distanceToEdge(vec2 p) { return minDist; } -float signedDistanceToPolygon(vec2 p) { - float dist = distanceToEdge(p); - bool inside = isPointInsidePolygon(p); - return inside ? dist : -dist; -} - void main() { vec2 pixel = fragTexCoord * resolution; - float signedDist = signedDistanceToPolygon(pixel); - + // Compute pixel size for anti-aliasing vec2 pixelGrad = vec2(dFdx(pixel.x), dFdy(pixel.y)); float pixelSize = length(pixelGrad); - float aaWidth = max(0.5, pixelSize * 0.5); // Sharper anti-aliasing + float aaWidth = max(0.5, pixelSize * 1.5); - float alpha = smoothstep(-aaWidth, aaWidth, signedDist); - if (alpha > 0.0) { - vec4 color = useGradient ? getGradientColor(fragTexCoord) : fillColor; + bool inside = isPointInsidePolygon(pixel); + if (inside) { + finalColor = useGradient == 1 ? getGradientColor(pixel) : fillColor; + return; + } + + float sd = -distanceToEdge(pixel); + float alpha = smoothstep(-aaWidth, aaWidth, sd); + if (alpha > 0.0){ + vec4 color = useGradient == 1 ? getGradientColor(pixel) : fillColor; finalColor = vec4(color.rgb, color.a * alpha); } else { finalColor = vec4(0.0); @@ -180,7 +161,6 @@ class ShaderState: 'gradientStops': None, 'gradientColorCount': None, 'mvp': None, - 'visibleGradientRange': None, } # Pre-allocated FFI objects @@ -191,7 +171,6 @@ class ShaderState: self.gradient_start_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.gradient_end_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.color_count_ptr = rl.ffi.new("int[]", [0]) - self.visible_gradient_range_ptr = rl.ffi.new("float[]", [0.0, 0.0]) self.gradient_colors_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS * 4) self.gradient_stops_ptr = rl.ffi.new("float[]", MAX_GRADIENT_COLORS) @@ -232,66 +211,40 @@ class ShaderState: self.initialized = False -def _configure_shader_color(state, color, gradient, rect, min_xy, max_xy): - """Configure shader uniforms for solid color or gradient rendering""" +def _configure_shader_color(state, color, gradient, clipped_rect, original_rect): use_gradient = 1 if gradient else 0 state.use_gradient_ptr[0] = use_gradient rl.set_shader_value(state.shader, state.locations['useGradient'], state.use_gradient_ptr, UNIFORM_INT) if use_gradient: - # Set gradient start/end - state.gradient_start_ptr[0:2] = gradient['start'] - state.gradient_end_ptr[0:2] = gradient['end'] + start = np.array(gradient['start']) * np.array([original_rect.width, original_rect.height]) + np.array([original_rect.x, original_rect.y]) + end = np.array(gradient['end']) * np.array([original_rect.width, original_rect.height]) + np.array([original_rect.x, original_rect.y]) + start = start - np.array([clipped_rect.x, clipped_rect.y]) + end = end - np.array([clipped_rect.x, clipped_rect.y]) + state.gradient_start_ptr[0:2] = start.astype(np.float32) + state.gradient_end_ptr[0:2] = end.astype(np.float32) rl.set_shader_value(state.shader, state.locations['gradientStart'], state.gradient_start_ptr, UNIFORM_VEC2) rl.set_shader_value(state.shader, state.locations['gradientEnd'], state.gradient_end_ptr, UNIFORM_VEC2) - # Calculate visible gradient range - width = max_xy[0] - min_xy[0] - height = max_xy[1] - min_xy[1] - - gradient_dir = (gradient['end'][0] - gradient['start'][0], gradient['end'][1] - gradient['start'][1]) - is_vertical = abs(gradient_dir[1]) > abs(gradient_dir[0]) - - visible_start = 0.0 - visible_end = 1.0 - - if is_vertical and height > 0: - visible_start = (rect.y - min_xy[1]) / height - visible_end = visible_start + rect.height / height - elif width > 0: - visible_start = (rect.x - min_xy[0]) / width - visible_end = visible_start + rect.width / width - - # Clamp visible range - visible_start = max(0.0, min(1.0, visible_start)) - visible_end = max(0.0, min(1.0, visible_end)) - - state.visible_gradient_range_ptr[0:2] = [visible_start, visible_end] - rl.set_shader_value(state.shader, state.locations['visibleGradientRange'], state.visible_gradient_range_ptr, UNIFORM_VEC2) - - # Set gradient colors colors = gradient['colors'] color_count = min(len(colors), MAX_GRADIENT_COLORS) + state.color_count_ptr[0] = color_count for i, c in enumerate(colors[:color_count]): base_idx = i * 4 state.gradient_colors_ptr[base_idx:base_idx+4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0] rl.set_shader_value_v(state.shader, state.locations['gradientColors'], state.gradient_colors_ptr, UNIFORM_VEC4, color_count) - # Set gradient stops - stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)]) - state.gradient_stops_ptr[0:color_count] = stops[:color_count] + stops = gradient.get('stops', [i / max(1, color_count - 1) for i in range(color_count)]) + stops = np.clip(stops[:color_count], 0.0, 1.0) + state.gradient_stops_ptr[0:color_count] = stops rl.set_shader_value_v(state.shader, state.locations['gradientStops'], state.gradient_stops_ptr, UNIFORM_FLOAT, color_count) - - # Set color count - state.color_count_ptr[0] = color_count rl.set_shader_value(state.shader, state.locations['gradientColorCount'], state.color_count_ptr, UNIFORM_INT) else: - color = color or rl.WHITE # Default to white if no color provided + color = color or rl.WHITE state.fill_color_ptr[0:4] = [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0] rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4) - -def draw_polygon(rect: rl.Rectangle, points: np.ndarray, color=None, gradient=None): +def draw_polygon(origin_rect: rl.Rectangle, points: np.ndarray, color=None, gradient=None): """ Draw a complex polygon using shader-based even-odd fill rule @@ -317,21 +270,16 @@ def draw_polygon(rect: rl.Rectangle, points: np.ndarray, color=None, gradient=No # Find bounding box min_xy = np.min(points, axis=0) max_xy = np.max(points, axis=0) - - # Clip coordinates to rectangle - clip_x = max(rect.x, min_xy[0]) - clip_y = max(rect.y, min_xy[1]) - clip_right = min(rect.x + rect.width, max_xy[0]) - clip_bottom = min(rect.y + rect.height, max_xy[1]) + clip_x = max(origin_rect.x, min_xy[0]) + clip_y = max(origin_rect.y, min_xy[1]) + clip_right = min(origin_rect.x + origin_rect.width, max_xy[0]) + clip_bottom = min(origin_rect.y + origin_rect.height, max_xy[1]) # Check if polygon is completely off-screen if clip_x >= clip_right or clip_y >= clip_bottom: return - clipped_width = clip_right - clip_x - clipped_height = clip_bottom - clip_y - - clip_rect = rl.Rectangle(clip_x, clip_y, clipped_width, clipped_height) + clipped_rect = rl.Rectangle(clip_x, clip_y, clip_right - clip_x, clip_bottom - clip_y) # Transform points relative to the CLIPPED area transformed_points = points - np.array([clip_x, clip_y]) @@ -340,21 +288,21 @@ def draw_polygon(rect: rl.Rectangle, points: np.ndarray, color=None, gradient=No state.point_count_ptr[0] = len(transformed_points) rl.set_shader_value(state.shader, state.locations['pointCount'], state.point_count_ptr, UNIFORM_INT) - state.resolution_ptr[0:2] = [clipped_width, clipped_height] + state.resolution_ptr[0:2] = [clipped_rect.width, clipped_rect.height] rl.set_shader_value(state.shader, state.locations['resolution'], state.resolution_ptr, UNIFORM_VEC2) flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32)) points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data) rl.set_shader_value_v(state.shader, state.locations['points'], points_ptr, UNIFORM_VEC2, len(transformed_points)) - _configure_shader_color(state, color, gradient, clip_rect, min_xy, max_xy) + _configure_shader_color(state, color, gradient, clipped_rect, origin_rect) # Render rl.begin_shader_mode(state.shader) rl.draw_texture_pro( state.white_texture, rl.Rectangle(0, 0, 2, 2), - clip_rect, + clipped_rect, rl.Vector2(0, 0), 0.0, rl.WHITE, From d2f417ee6368f50b7ea87369c5b87fe0709fa950 Mon Sep 17 00:00:00 2001 From: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Date: Sat, 31 May 2025 14:17:55 -0300 Subject: [PATCH 393/477] Multilang: update pt-BR translation. (#35394) Multilang: update pt-BR translations --- selfdrive/ui/translations/main_pt-BR.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index f98f32b9a8..3182370f42 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -84,7 +84,7 @@ Prevent large data uploads when on a metered cellular connection - + Previna o envio de grandes volumes de dados em conexões de celular com franquia de limite de dados default @@ -104,7 +104,7 @@ Prevent large data uploads when on a metered Wi-Fi connection - + Previna o envio de grandes volumes de dados em conexões Wi-Fi com franquia de limite de dados From e9f917836bd004fb1e343851b7c4e88ad64521b1 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 31 May 2025 10:26:03 -0700 Subject: [PATCH 394/477] cereal: fixup build deps --- cereal/SConscript | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cereal/SConscript b/cereal/SConscript index a58a9490ce..9fccfc6075 100644 --- a/cereal/SConscript +++ b/cereal/SConscript @@ -1,19 +1,20 @@ -Import('env', 'common', 'msgq') +Import('env', 'msgq') cereal_dir = Dir('.') gen_dir = Dir('gen') # Build cereal schema_files = ['log.capnp', 'car.capnp', 'legacy.capnp', 'custom.capnp'] -env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files], +schema_cpp = [f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files] +env.Command(schema_cpp, schema_files, f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/") -cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files]) +cereal = env.Library('cereal', schema_cpp) # Build messaging services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET') -env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, common, 'pthread']) +env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, 'zmq', 'pthread']) socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc']) From e734413a2170a2c10a90ed9821f460e8372f2348 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Sun, 1 Jun 2025 01:50:41 +0800 Subject: [PATCH 395/477] system/ui: fix path clip issues (#35409) fix path clip issues --- system/ui/onroad/model_renderer.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index ef7e567cda..a016f1cec6 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -105,13 +105,13 @@ class ModelRenderer: if model_updated: self._update_raw_points(model) - pos_x_array = self._path.raw_points[:, 0] - if pos_x_array.size == 0: + path_x_array = self._path.raw_points[:, 0] + if path_x_array.size == 0: return - self._update_model(lead_one, pos_x_array) + self._update_model(lead_one, path_x_array) if render_lead_indicator: - self._update_leads(radar_state, pos_x_array) + self._update_leads(radar_state, path_x_array) self._transform_dirty = False @@ -136,23 +136,23 @@ class ModelRenderer: self._road_edge_stds = np.array(model.roadEdgeStds, dtype=np.float32) self._acceleration_x = np.array(model.acceleration.x, dtype=np.float32) - def _update_leads(self, radar_state, pos_x_array): + def _update_leads(self, radar_state, path_x_array): """Update positions of lead vehicles""" self._lead_vehicles = [LeadVehicle(), LeadVehicle()] leads = [radar_state.leadOne, radar_state.leadTwo] for i, lead_data in enumerate(leads): if lead_data and lead_data.status: d_rel, y_rel, v_rel = lead_data.dRel, lead_data.yRel, lead_data.vRel - idx = self._get_path_length_idx(pos_x_array, d_rel) + idx = self._get_path_length_idx(path_x_array, d_rel) z = self._path.raw_points[idx, 2] if idx < len(self._path.raw_points) else 0.0 point = self._map_to_screen(d_rel, -y_rel, z + self._path_offset_z) if point: self._lead_vehicles[i] = self._update_lead_vehicle(d_rel, v_rel, point, self._rect) - def _update_model(self, lead, pos_x_array): + def _update_model(self, lead, path_x_array): """Update model visualization data based on model message""" - max_distance = np.clip(pos_x_array[-1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE) - max_idx = self._get_path_length_idx(pos_x_array, max_distance) + max_distance = np.clip(path_x_array[-1], MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE) + max_idx = self._get_path_length_idx(self._lane_lines[0].raw_points[:, 0], max_distance) # Update lane lines using raw points for i, lane_line in enumerate(self._lane_lines): @@ -168,8 +168,8 @@ class ModelRenderer: if lead and lead.status: lead_d = lead.dRel * 2.0 max_distance = np.clip(lead_d - min(lead_d * 0.35, 10.0), 0.0, max_distance) - max_idx = self._get_path_length_idx(pos_x_array, max_distance) + max_idx = self._get_path_length_idx(path_x_array, max_distance) self._path.projected_points = self._map_line_to_polygon( self._path.raw_points, 0.9, self._path_offset_z, max_idx, allow_invert=False ) @@ -309,8 +309,10 @@ class ModelRenderer: @staticmethod def _get_path_length_idx(pos_x_array: np.ndarray, path_height: float) -> int: """Get the index corresponding to the given path height""" - idx = np.searchsorted(pos_x_array, path_height, side='right') - return int(np.clip(idx - 1, 0, len(pos_x_array) - 1)) + if len(pos_x_array) == 0: + return 0 + indices = np.where(pos_x_array <= path_height)[0] + return indices[-1] if indices.size > 0 else 0 def _map_to_screen(self, in_x, in_y, in_z): """Project a point in car space to screen space""" From c2bd95c2af2a4979c2779c4350a685bd08f87699 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 31 May 2025 14:49:40 -0400 Subject: [PATCH 396/477] Tinygrad: revert bump (#969) --- tinygrad_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinygrad_repo b/tinygrad_repo index 76eb130d8c..519dec6677 160000 --- a/tinygrad_repo +++ b/tinygrad_repo @@ -1 +1 @@ -Subproject commit 76eb130d8c6567679641ff53a7215d1f6f88eb9a +Subproject commit 519dec6677f98718ee4f2d07be1936eb91dde73b From e50f7513a62018c92d138aeab46849be6cba4e7d Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 1 Jun 2025 10:00:26 -0400 Subject: [PATCH 397/477] Update Python packages (#970) --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 14e5afd7ab..bb37b56789 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 14e5afd7ab80f385d01298d9fb2fd71c358e69b4 +Subproject commit bb37b56789abd13b6cdd9ff251df9c644a050223 From 5605c398a20d8206d235abee53264921d781da58 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 1 Jun 2025 12:37:12 -0700 Subject: [PATCH 398/477] Revert "cereal: fixup build deps" This reverts commit e9f917836bd004fb1e343851b7c4e88ad64521b1. --- cereal/SConscript | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cereal/SConscript b/cereal/SConscript index 9fccfc6075..a58a9490ce 100644 --- a/cereal/SConscript +++ b/cereal/SConscript @@ -1,20 +1,19 @@ -Import('env', 'msgq') +Import('env', 'common', 'msgq') cereal_dir = Dir('.') gen_dir = Dir('gen') # Build cereal schema_files = ['log.capnp', 'car.capnp', 'legacy.capnp', 'custom.capnp'] -schema_cpp = [f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files] -env.Command(schema_cpp, +env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files], schema_files, f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/") -cereal = env.Library('cereal', schema_cpp) +cereal = env.Library('cereal', [f'gen/cpp/{s}.c++' for s in schema_files]) # Build messaging services_h = env.Command(['services.h'], ['services.py'], 'python3 ' + cereal_dir.path + '/services.py > $TARGET') -env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, 'zmq', 'pthread']) +env.Program('messaging/bridge', ['messaging/bridge.cc', 'messaging/msgq_to_zmq.cc'], LIBS=[msgq, common, 'pthread']) socketmaster = env.Library('socketmaster', ['messaging/socketmaster.cc']) From 1935871267bce0cabf2f7bba669d56c959a338b9 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 03:37:36 +0800 Subject: [PATCH 399/477] system/ui: add stream switching capability to CameraView (#35414) add stream switching capability to CameraView --- system/ui/onroad/augmented_road_view.py | 14 ++++++++------ system/ui/widgets/cameraview.py | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 18f94325f4..053307bb69 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -30,9 +30,6 @@ class AugmentedRoadView(CameraView): super().__init__("camerad", stream_type) self.sm = sm - self.stream_type = stream_type - self.is_wide_camera = stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD - self.device_camera: DeviceCameraConfig | None = None self.view_from_calib = view_frame_from_device_frame.copy() self.view_from_wide_calib = view_frame_from_device_frame.copy() @@ -130,9 +127,10 @@ class AugmentedRoadView(CameraView): # Get camera configuration device_camera = self.device_camera or DEFAULT_DEVICE_CAMERA - intrinsic = device_camera.ecam.intrinsics if self.is_wide_camera else device_camera.fcam.intrinsics - calibration = self.view_from_wide_calib if self.is_wide_camera else self.view_from_calib - zoom = 2.0 if self.is_wide_camera else 1.1 + is_wide_camera = self.stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD + intrinsic = device_camera.ecam.intrinsics if is_wide_camera else device_camera.fcam.intrinsics + calibration = self.view_from_wide_calib if is_wide_camera else self.view_from_calib + zoom = 2.0 if is_wide_camera else 1.1 # Calculate transforms for vanishing point inf_point = np.array([1000.0, 0.0, 0.0]) @@ -184,9 +182,13 @@ if __name__ == "__main__": "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", "roadCameraState", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan"]) road_camera_view = AugmentedRoadView(sm, VisionStreamType.VISION_STREAM_ROAD) + print("***press space to switch camera view***") try: for _ in gui_app.render(): sm.update(0) + if rl.is_key_released(rl.KeyboardKey.KEY_SPACE): + is_wide = road_camera_view.stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD + road_camera_view.switch_stream(VisionStreamType.VISION_STREAM_ROAD if is_wide else VisionStreamType.VISION_STREAM_WIDE_ROAD) road_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) finally: road_camera_view.close() diff --git a/system/ui/widgets/cameraview.py b/system/ui/widgets/cameraview.py index 49832444f8..329b8349ba 100644 --- a/system/ui/widgets/cameraview.py +++ b/system/ui/widgets/cameraview.py @@ -3,6 +3,7 @@ import pyray as rl from openpilot.system.hardware import TICI from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf +from openpilot.common.swaglog import cloudlog from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage @@ -57,6 +58,9 @@ else: class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): self.client = VisionIpcClient(name, stream_type, False) + self._name = name + self._stream_type = stream_type + self._texture_needs_update = True self.last_connection_attempt: float = 0.0 self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER) @@ -80,6 +84,18 @@ class CameraView: self.egl_texture = rl.load_texture_from_image(temp_image) rl.unload_image(temp_image) + def switch_stream(self, stream_type: VisionStreamType) -> None: + if self._stream_type != stream_type: + cloudlog.debug(f'switching stream from {self._stream_type} to {stream_type}') + self._clear_textures() + self.frame = None + self._stream_type = stream_type + self.client = VisionIpcClient(self._name, stream_type, False) + + @property + def stream_type(self) -> VisionStreamType: + return self._stream_type + def close(self) -> None: self._clear_textures() @@ -92,6 +108,8 @@ class CameraView: if self.shader and self.shader.id: rl.unload_shader(self.shader) + self.client = None + def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: if not self.frame: return np.eye(3) @@ -201,12 +219,12 @@ class CameraView: current_time = rl.get_time() if current_time - self.last_connection_attempt < CONNECTION_RETRY_INTERVAL: return False - self.last_connection_attempt = current_time if not self.client.connect(False) or not self.client.num_buffers: return False + cloudlog.debug(f"Connected to {self._name} stream: {self._stream_type}, buffers: {self.client.num_buffers}") self._clear_textures() if not TICI: From 74541e677c62fa6068dc7bdba8681dfb59e5e734 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 03:37:57 +0800 Subject: [PATCH 400/477] system/ui: fix timeout logc and add pre-defined alerts (#35417) fix timeout logc and add pre-defined alerts --- system/ui/onroad/alert_renderer.py | 115 +++++++++++++---------------- 1 file changed, 51 insertions(+), 64 deletions(-) diff --git a/system/ui/onroad/alert_renderer.py b/system/ui/onroad/alert_renderer.py index 600c721680..947b19663d 100644 --- a/system/ui/onroad/alert_renderer.py +++ b/system/ui/onroad/alert_renderer.py @@ -1,14 +1,15 @@ -import numpy as np +import time import pyray as rl from dataclasses import dataclass from cereal import messaging, log +from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight # Constants ALERT_COLORS = { - log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 150), # Black - log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 100), # Orange - log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 150), # Red + log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 220), # Black + log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 220), # Orange + log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 220), # Red } ALERT_HEIGHTS = { @@ -16,6 +17,7 @@ ALERT_HEIGHTS = { log.SelfdriveState.AlertSize.mid: 420, } +ALERT_BORDER_RADIUS = 30 SELFDRIVE_STATE_TIMEOUT = 5 # Seconds SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds @@ -28,95 +30,80 @@ class Alert: size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal - def is_equal(self, other: 'Alert') -> bool: - """Check if two alerts are equal.""" - return ( - self.text1 == other.text1 - and self.text2 == other.text2 - and self.alert_type == other.alert_type - and self.size == other.size - and self.status == other.status - ) + +# Pre-defined alert instances +ALERT_STARTUP_PENDING = Alert( + text1="openpilot Unavailable", + text2="Waiting to start", + alert_type="selfdriveWaiting", + size=log.SelfdriveState.AlertSize.mid, + status=log.SelfdriveState.AlertStatus.normal, +) + +ALERT_CRITICAL_TIMEOUT = Alert( + text1="TAKE CONTROL IMMEDIATELY", + text2="System Unresponsive", + alert_type="selfdriveUnresponsive", + size=log.SelfdriveState.AlertSize.full, + status=log.SelfdriveState.AlertStatus.critical, +) + +ALERT_CRITICAL_REBOOT = Alert( + text1="System Unresponsive", + text2="Reboot Device", + alert_type="selfdriveUnresponsivePermanent", + size=log.SelfdriveState.AlertSize.full, + status=log.SelfdriveState.AlertStatus.critical, +) class AlertRenderer: def __init__(self): """Initialize the alert renderer.""" self.alert: Alert = Alert() + # TODO: use ui_state to determine when to start self.started_frame: int = 0 self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {} - def clear(self) -> None: - """Reset the alert to its default state.""" - self.alert = Alert() - - def update_state(self, sm: messaging.SubMaster, started_frame: int) -> None: + def update_state(self, sm: messaging.SubMaster) -> None: """Update alert state based on SubMaster data.""" - self.started_frame = started_frame - new_alert = self.get_alert(sm) - if not self.alert.is_equal(new_alert): - self.alert = new_alert + self.alert = self.get_alert(sm) def get_alert(self, sm: messaging.SubMaster) -> Alert: """Generate the current alert based on selfdrive state.""" - if not sm.valid['selfdriveState']: - return Alert() - ss = sm['selfdriveState'] - selfdrive_frame = sm.recv_frame['selfdriveState'] - alert_status = self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus) - # Return current alert if selfdrive state is recent - if selfdrive_frame >= self.started_frame: - return Alert( + # Check if waiting to start + if sm.recv_frame['selfdriveState'] < self.started_frame: + return ALERT_STARTUP_PENDING + + # Handle selfdrive timeout + ss_missing = time.monotonic() - sm.recv_time['selfdriveState'] + if TICI: + if ss_missing > SELFDRIVE_STATE_TIMEOUT: + if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT: + return ALERT_CRITICAL_TIMEOUT + return ALERT_CRITICAL_REBOOT + + # Return current alert from selfdrive state + return Alert( text1=ss.alertText1, text2=ss.alertText2, alert_type=ss.alertType, size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize), - status=alert_status, - ) - - # Handle selfdrive timeout - ss_missing = (np.uint64(rl.get_time() * 1e9) - sm.recv_time['selfdriveState']) / 1e9 - if selfdrive_frame < self.started_frame: - return Alert( - text1="openpilot Unavailable", - text2="Waiting to start", - alert_type="selfdriveWaiting", - size=log.SelfdriveState.AlertSize.mid, - status=log.SelfdriveState.AlertStatus.normal, - ) - elif ss_missing > SELFDRIVE_STATE_TIMEOUT: - if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT: - return Alert( - text1="TAKE CONTROL IMMEDIATELY", - text2="System Unresponsive", - alert_type="selfdriveUnresponsive", - size=log.SelfdriveState.AlertSize.full, - status=log.SelfdriveState.AlertStatus.critical, - ) - return Alert( - text1="System Unresponsive", - text2="Reboot Device", - alert_type="selfdriveUnresponsivePermanent", - size=log.SelfdriveState.AlertSize.mid, - status=log.SelfdriveState.AlertStatus.normal, - ) - - return Alert() + status=self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus)) def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None: """Render the alert within the specified rectangle.""" - self.update_state(sm, sm.recv_frame['selfdriveState']) + self.update_state(sm) alert_size = self._get_enum_value(self.alert.size, log.SelfdriveState.AlertSize) if alert_size == log.SelfdriveState.AlertSize.none: return # Calculate alert rectangle margin = 0 if alert_size == log.SelfdriveState.AlertSize.full else 40 - radius = 0 if alert_size == log.SelfdriveState.AlertSize.full else 30 height = ALERT_HEIGHTS.get(alert_size, rect.height) alert_rect = rl.Rectangle( rect.x + margin, @@ -129,7 +116,7 @@ class AlertRenderer: alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus) color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal]) if alert_size != log.SelfdriveState.AlertSize.full: - roundness = radius / (min(alert_rect.width, alert_rect.height) / 2) + roundness = ALERT_BORDER_RADIUS / (min(alert_rect.width, alert_rect.height) / 2) rl.draw_rectangle_rounded(alert_rect, roundness, 10, color) else: rl.draw_rectangle_rec(alert_rect, color) From 79f3f30c6367a06f0e3b83c042d49b3e98a550b8 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 03:39:09 +0800 Subject: [PATCH 401/477] =?UTF-8?q?system/ui:=20fix=20cruise=20disabled=20?= =?UTF-8?q?state=20displaying=20"=3F"=20instead=20of=20"=E2=80=93"=20(#354?= =?UTF-8?q?16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix curise disabled state showed '?' --- system/ui/lib/application.py | 2 ++ system/ui/onroad/hud_renderer.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 9bb56fec8d..a8b55b9e79 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -192,10 +192,12 @@ class GuiApplication: # Create a character set from our keyboard layouts from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS + from openpilot.system.ui.onroad.hud_renderer import CRUISE_DISABLED_CHAR all_chars = set() for layout in KEYBOARD_LAYOUTS.values(): all_chars.update(key for row in layout for key in row) all_chars = "".join(all_chars) + all_chars += CRUISE_DISABLED_CHAR codepoint_count = rl.ffi.new("int *", 1) codepoints = rl.load_codepoints(all_chars, codepoint_count) diff --git a/system/ui/onroad/hud_renderer.py b/system/ui/onroad/hud_renderer.py index b63b50f26d..ee182daef8 100644 --- a/system/ui/onroad/hud_renderer.py +++ b/system/ui/onroad/hud_renderer.py @@ -8,6 +8,7 @@ from enum import IntEnum # Constants SET_SPEED_NA = 255 KM_TO_MILE = 0.621371 +CRUISE_DISABLED_CHAR = '–' @dataclass(frozen=True) @@ -153,7 +154,7 @@ class HudRenderer: max_color, ) - set_speed_text = "–" if not self.is_cruise_set else str(round(self.set_speed)) + set_speed_text = CRUISE_DISABLED_CHAR if not self.is_cruise_set else str(round(self.set_speed)) speed_text_width = self._measure_text(set_speed_text, self._font_bold, FONT_SIZES.set_speed, 'bold').x rl.draw_text_ex( self._font_bold, From 6ece69610bbe2336d3067c4e18998a323547562e Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 03:39:22 +0800 Subject: [PATCH 402/477] systme/ui: add global ARC_POINT_COUNT and ARC_ANGLES to DriverState (#35415) optimize arc rRendering with Pre-computed values --- system/ui/onroad/driver_state.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/system/ui/onroad/driver_state.py b/system/ui/onroad/driver_state.py index b998936903..7527fa6255 100644 --- a/system/ui/onroad/driver_state.py +++ b/system/ui/onroad/driver_state.py @@ -27,6 +27,9 @@ ARC_THICKNESS_EXTEND = 12.0 SCALES_POS = np.array([0.9, 0.4, 0.4], dtype=np.float32) SCALES_NEG = np.array([0.7, 0.4, 0.4], dtype=np.float32) +ARC_POINT_COUNT = 37 # Number of points in the arc +ARC_ANGLES = np.linspace(0.0, np.pi, ARC_POINT_COUNT, dtype=np.float32) + @dataclass class ArcData: """Data structure for arc rendering parameters.""" @@ -57,8 +60,8 @@ class DriverStateRenderer: # Pre-allocate drawing arrays self.face_lines = [rl.Vector2(0, 0) for _ in range(len(DEFAULT_FACE_KPTS_3D))] - self.h_arc_lines = [rl.Vector2(0, 0) for _ in range(37)] # 37 points for horizontal arc - self.v_arc_lines = [rl.Vector2(0, 0) for _ in range(37)] # 37 points for vertical arc + self.h_arc_lines = [rl.Vector2(0, 0) for _ in range(ARC_POINT_COUNT)] + self.v_arc_lines = [rl.Vector2(0, 0) for _ in range(ARC_POINT_COUNT)] # Load the driver face icon self.dm_img = gui_app.texture("icons/driver_face.png", IMG_SIZE, IMG_SIZE) @@ -218,9 +221,7 @@ class DriverStateRenderer: ) # Pre-calculate arc points - start_rad = np.deg2rad(start_angle) - end_rad = np.deg2rad(start_angle + 180) - angles = np.linspace(start_rad, end_rad, 37) + angles = ARC_ANGLES + np.deg2rad(start_angle) center_x = x + arc_data.width / 2 center_y = y + arc_data.height / 2 From 1e702de434f10e80c22f7424d6c41edb8c2edf30 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 03:39:41 +0800 Subject: [PATCH 403/477] system/ui: add face detection box and driver state icon to DriverCameraView (#35410) add face detection box and driver state icon to DriverCameraView --- system/ui/widgets/driver_camera_view.py | 56 +++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/system/ui/widgets/driver_camera_view.py b/system/ui/widgets/driver_camera_view.py index 174fac378d..56dcbf2b9e 100644 --- a/system/ui/widgets/driver_camera_view.py +++ b/system/ui/widgets/driver_camera_view.py @@ -1,18 +1,63 @@ import numpy as np import pyray as rl +from cereal import messaging from openpilot.system.ui.widgets.cameraview import CameraView from msgq.visionipc import VisionStreamType -from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.label import gui_label +from openpilot.system.ui.onroad.driver_state import DriverStateRenderer class DriverCameraView(CameraView): def __init__(self, stream_type: VisionStreamType): super().__init__("camerad", stream_type) + self.driver_state_renderer = DriverStateRenderer() - def render(self, rect): + def render(self, rect, sm): super().render(rect) - # TODO: Add additional rendering logic + if not self.frame: + gui_label( + rect, + "camera starting", + font_size=100, + font_weight=FontWeight.BOLD, + alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + ) + return + + self._draw_face_detection(rect, sm) + self.driver_state_renderer.draw(rect, sm) + + def _draw_face_detection(self, rect: rl.Rectangle, sm) -> None: + driver_state = sm["driverStateV2"] + is_rhd = driver_state.wheelOnRightProb > 0.5 + driver_data = driver_state.rightDriverData if is_rhd else driver_state.leftDriverData + face_detect = driver_data.faceProb > 0.7 + if not face_detect: + return + + # Get face position and orientation + face_x, face_y = driver_data.facePosition + face_std = max(driver_data.faceOrientationStd[0], driver_data.faceOrientationStd[1]) + alpha = 0.7 + if face_std > 0.15: + alpha = max(0.7 - (face_std - 0.15) * 3.5, 0.0) + + # use approx instead of distort_points + # TODO: replace with distort_points + fbox_x = int(1080.0 - 1714.0 * face_x) + fbox_y = int(-135.0 + (504.0 + abs(face_x) * 112.0) + (1205.0 - abs(face_x) * 724.0) * face_y) + box_size = 220 + + line_color = rl.Color(255, 255, 255, int(alpha * 255)) + rl.draw_rectangle_rounded_lines_ex( + rl.Rectangle(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size), + 35.0 / box_size / 2, + 10, + 10, + line_color, + ) def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: driver_view_ratio = 2.0 @@ -38,9 +83,12 @@ class DriverCameraView(CameraView): if __name__ == "__main__": gui_app.init_window("Driver Camera View") + sm = messaging.SubMaster(["selfdriveState", "driverStateV2", "driverMonitoringState"]) + driver_camera_view = DriverCameraView(VisionStreamType.VISION_STREAM_DRIVER) try: for _ in gui_app.render(): - driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + sm.update() + driver_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height), sm) finally: driver_camera_view.close() From ad58fea2f0a6a3ba30b7250ecec22163ee3e6822 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 1 Jun 2025 16:34:22 -0700 Subject: [PATCH 404/477] no more pylint (#35418) * no more pylint * bump opendbc --- common/logging_extra.py | 1 - opendbc_repo | 2 +- panda | 2 +- system/manager/process.py | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/common/logging_extra.py b/common/logging_extra.py index e921b140c2..ceaf083cd5 100644 --- a/common/logging_extra.py +++ b/common/logging_extra.py @@ -199,7 +199,6 @@ class SwagLogger(logging.Logger): co = f.f_code filename = os.path.normcase(co.co_filename) - # TODO: is this pylint exception correct? if filename == _srcfile: f = f.f_back continue diff --git a/opendbc_repo b/opendbc_repo index d33a033a7f..4fe147125a 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit d33a033a7fceaf660a1ae1e6e7d00fb4737f6745 +Subproject commit 4fe147125a6193a6133daefde7bd9c4c871afc1f diff --git a/panda b/panda index 4f227f88c8..c19692245c 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 4f227f88c827d02188763d676aab941401be8212 +Subproject commit c19692245cfe681389a1e9ddb2401340cf08759d diff --git a/system/manager/process.py b/system/manager/process.py index 6c233c21ab..7f8044461d 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -96,7 +96,6 @@ class ManagerProcess(ABC): try: fn = WATCHDOG_FN + str(self.proc.pid) with open(fn, "rb") as f: - # TODO: why can't pylint find struct.unpack? self.last_watchdog_time = struct.unpack('Q', f.read())[0] except Exception: pass From a31a39624c6f9ecffe60c7555084fe60fdbc24b2 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sun, 1 Jun 2025 22:46:04 -0400 Subject: [PATCH 405/477] Car: `CarStateSP` (#972) * Car: `CarStateSP` * fix tests * just skeleton for now * bump --- cereal/custom.capnp | 2 +- cereal/log.capnp | 2 +- cereal/services.py | 1 + opendbc_repo | 2 +- selfdrive/car/car_specific.py | 6 +++--- selfdrive/car/card.py | 24 ++++++++++++++++-------- selfdrive/car/helpers.py | 4 +++- selfdrive/car/tests/test_models.py | 7 ++++--- 8 files changed, 30 insertions(+), 18 deletions(-) diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 8ae77875f2..8a42f8b6a5 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -223,7 +223,7 @@ struct BackupManagerSP @0xf98d843bfd7004a3 { } } -struct CustomReserved7 @0xb86e6369214c01c8 { +struct CarStateSP @0xb86e6369214c01c8 { } struct CustomReserved8 @0xf416ec09499d9d19 { diff --git a/cereal/log.capnp b/cereal/log.capnp index 79f11f6bcf..412660bc6a 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -2609,7 +2609,7 @@ struct Event { carParamsSP @111 :Custom.CarParamsSP; carControlSP @112 :Custom.CarControlSP; backupManagerSP @113 :Custom.BackupManagerSP; - customReserved7 @114 :Custom.CustomReserved7; + carStateSP @114 :Custom.CarStateSP; customReserved8 @115 :Custom.CustomReserved8; customReserved9 @116 :Custom.CustomReserved9; customReserved10 @136 :Custom.CustomReserved10; diff --git a/cereal/services.py b/cereal/services.py index c40538eec0..4097e484f6 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -83,6 +83,7 @@ _services: dict[str, tuple] = { "onroadEventsSP": (True, 1., 1), "carParamsSP": (True, 0.02, 1), "carControlSP": (True, 100., 10), + "carStateSP": (True, 100., 10), # debug "uiDebug": (True, 0., 1), diff --git a/opendbc_repo b/opendbc_repo index bb37b56789..a82c8d83cd 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit bb37b56789abd13b6cdd9ff251df9c644a050223 +Subproject commit a82c8d83cd776dc36f84568f00e9adda73d8a563 diff --git a/selfdrive/car/car_specific.py b/selfdrive/car/car_specific.py index d441c648ff..151a8479a7 100644 --- a/selfdrive/car/car_specific.py +++ b/selfdrive/car/car_specific.py @@ -1,4 +1,4 @@ -from cereal import car, log +from cereal import car, log, custom import cereal.messaging as messaging from opendbc.car import DT_CTRL, structs from opendbc.car.interfaces import MAX_CTRL_SPEED @@ -16,14 +16,14 @@ class MockCarState: def __init__(self): self.sm = messaging.SubMaster(['gpsLocation', 'gpsLocationExternal']) - def update(self, CS: car.CarState): + def update(self, CS: car.CarState, CS_SP: custom.CarStateSP): self.sm.update(0) gps_sock = 'gpsLocationExternal' if self.sm.recv_frame['gpsLocationExternal'] > 1 else 'gpsLocation' CS.vEgo = self.sm[gps_sock].speed CS.vEgoRaw = self.sm[gps_sock].speed - return CS + return CS, CS_SP class CarSpecificEvents: diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 62dc818cd9..1b9a1522a1 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -73,12 +73,13 @@ class Car: def __init__(self, CI=None, RI=None) -> None: self.can_sock = messaging.sub_sock('can', timeout=20) self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'] + ['carControlSP']) - self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'liveTracks'] + ['carParamsSP']) + self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'liveTracks'] + ['carParamsSP', 'carStateSP']) self.can_rcv_cum_timeout_counter = 0 self.CC_prev = car.CarControl.new_message() self.CS_prev = car.CarState.new_message() + self.CS_SP_prev = custom.CarStateSP.new_message() self.initialized_prev = False self.last_actuators_output = structs.CarControl.Actuators() @@ -188,16 +189,17 @@ class Car: # log fingerprint in sentry sunnypilot_interfaces.log_fingerprint(self.CP) - def state_update(self) -> tuple[car.CarState, structs.RadarDataT | None]: + def state_update(self) -> tuple[car.CarState, custom.CarStateSP, structs.RadarDataT | None]: """carState update loop, driven by can""" can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) can_list = can_capnp_to_list(can_strs) # Update carState from CAN - CS = self.CI.update(can_list) + CS, CS_SP = self.CI.update(can_list) + CS_SP = convert_to_capnp(CS_SP) if self.CP.brand == 'mock': - CS = self.mock_carstate.update(CS) + CS, CS_SP = self.mock_carstate.update(CS, CS_SP) # Update radar tracks from CAN RD: structs.RadarDataT | None = self.RI.update(can_list) @@ -222,9 +224,9 @@ class Car: CS.vCruise = float(self.v_cruise_helper.v_cruise_kph) CS.vCruiseCluster = float(self.v_cruise_helper.v_cruise_cluster_kph) - return CS, RD + return CS, CS_SP, RD - def state_publish(self, CS: car.CarState, RD: structs.RadarDataT | None): + def state_publish(self, CS: car.CarState, CS_SP: custom.CarStateSP, RD: structs.RadarDataT | None): """carState and carParams publish loop""" # carParams - logged every 50 seconds (> 1 per segment) @@ -261,6 +263,11 @@ class Car: cp_sp_send.carParamsSP = self.CP_SP_capnp self.pm.send('carParamsSP', cp_sp_send) + cs_sp_send = messaging.new_message('carStateSP') + cs_sp_send.valid = CS.canValid + cs_sp_send.carStateSP = CS_SP + self.pm.send('carStateSP', cs_sp_send) + def controls_update(self, CS: car.CarState, CC: car.CarControl, CC_SP: custom.CarControlSP): """control update loop, driven by carControl""" @@ -280,9 +287,9 @@ class Car: self.CC_prev = CC def step(self): - CS, RD = self.state_update() + CS, CS_SP, RD = self.state_update() - self.state_publish(CS, RD) + self.state_publish(CS, CS_SP, RD) initialized = (not any(e.name == EventName.selfdriveInitializing for e in self.sm['onroadEvents']) and self.sm.seen['onroadEvents']) @@ -291,6 +298,7 @@ class Car: self.initialized_prev = initialized self.CS_prev = CS + self.CS_SP_prev = CS_SP def params_thread(self, evt): while not evt.is_set(): diff --git a/selfdrive/car/helpers.py b/selfdrive/car/helpers.py index 6ebacfc976..6187be1474 100644 --- a/selfdrive/car/helpers.py +++ b/selfdrive/car/helpers.py @@ -35,11 +35,13 @@ def asdictref(obj) -> dict[str, Any]: return _asdictref_inner(obj) -def convert_to_capnp(struct: structs.CarParamsSP) -> capnp.lib.capnp._DynamicStructBuilder: +def convert_to_capnp(struct: structs.CarParamsSP | structs.CarStateSP) -> capnp.lib.capnp._DynamicStructBuilder: struct_dict = asdictref(struct) if isinstance(struct, structs.CarParamsSP): struct_capnp = custom.CarParamsSP.new_message(**struct_dict) + elif isinstance(struct, structs.CarStateSP): + struct_capnp = custom.CarStateSP.new_message(**struct_dict) else: raise ValueError(f"Unsupported struct type: {type(struct)}") diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index a880e06fca..0fc6716dd3 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -204,7 +204,7 @@ class TestCarModelBase(unittest.TestCase): CC_SP = structs.CarControlSP() for i, msg in enumerate(self.can_msgs): - CS = self.CI.update(msg) + CS, _ = self.CI.update(msg) self.CI.apply(CC, CC_SP, msg[0]) if CS.canValid: @@ -349,7 +349,7 @@ class TestCarModelBase(unittest.TestCase): self.safety.safety_rx_hook(to_send) can = [(int(time.monotonic() * 1e9), [CanData(address=address, dat=dat, src=bus)])] - CS = self.CI.update(can) + CS, _ = self.CI.update(can) if self.safety.get_gas_pressed_prev() != prev_panda_gas: self.assertEqual(CS.gasPressed, self.safety.get_gas_pressed_prev()) @@ -409,7 +409,8 @@ class TestCarModelBase(unittest.TestCase): checks = defaultdict(int) vehicle_speed_seen = self.CP.steerControlType == SteerControlType.angle and not self.CP.notCar for idx, can in enumerate(self.can_msgs): - CS = self.CI.update(can).as_reader() + CS, _ = self.CI.update(can) + CS = CS.as_reader() for msg in filter(lambda m: m.src < 64, can[1]): to_send = libsafety_py.make_CANPacket(msg.address, msg.src % 4, msg.dat) ret = self.safety.safety_rx_hook(to_send) From 69076c50d88d3858357ac1873a1aaff6f8ff022e Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sun, 1 Jun 2025 23:15:23 -0700 Subject: [PATCH 406/477] bump opendbc (#35419) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 4fe147125a..f01c4b82b1 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4fe147125a6193a6133daefde7bd9c4c871afc1f +Subproject commit f01c4b82b1cd41ef8053a038ad631bbd31105fc6 From d488529a94f05f08b7d69e5c28938aaa016ba406 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 23:48:35 +0800 Subject: [PATCH 407/477] system/ui: add font_weight parameter to gui_text_box (#35420) add font_weight parameter to gui_text_box --- system/ui/lib/label.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/system/ui/lib/label.py b/system/ui/lib/label.py index 5244c6baf2..00a4eda7c6 100644 --- a/system/ui/lib/label.py +++ b/system/ui/lib/label.py @@ -56,7 +56,8 @@ def gui_text_box( font_size: int = DEFAULT_TEXT_SIZE, color: rl.Color = DEFAULT_TEXT_COLOR, alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_LEFT, - alignment_vertical: int = rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP + alignment_vertical: int = rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP, + font_weight: FontWeight = FontWeight.NORMAL, ): styles = [ (rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(color)), @@ -66,6 +67,12 @@ def gui_text_box( (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_ALIGNMENT_VERTICAL, alignment_vertical), (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_WRAP_MODE, rl.GuiTextWrapMode.TEXT_WRAP_WORD) ] + if font_weight != FontWeight.NORMAL: + rl.gui_set_font(gui_app.font(font_weight)) with GuiStyleContext(styles): rl.gui_label(rect, text) + + if font_weight != FontWeight.NORMAL: + rl.gui_set_font(gui_app.font(FontWeight.NORMAL)) + From 070711426459f5ae3a3354be9a822bbe0b8d52c1 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Mon, 2 Jun 2025 23:56:47 +0800 Subject: [PATCH 408/477] system/ui: optimize point allocation, cllipping, and HSLA Color Conversion in model renderer (#35423) * faster hsla_to_color * pre-allc points * use np.clip * re-alloc points --- system/ui/onroad/model_renderer.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index a016f1cec6..44bd3f72ca 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -14,6 +14,8 @@ MAX_DRAW_DISTANCE = 100.0 PATH_COLOR_TRANSITION_DURATION = 0.5 # Seconds for color transition animation PATH_BLEND_INCREMENT = 1.0 / (PATH_COLOR_TRANSITION_DURATION * DEFAULT_FPS) +MAX_POINTS = 200 + THROTTLE_COLORS = [ rl.Color(13, 248, 122, 102), # HSLF(148/360, 0.94, 0.51, 0.4) rl.Color(114, 255, 92, 89), # HSLF(112/360, 1.0, 0.68, 0.35) @@ -61,6 +63,11 @@ class ModelRenderer: self._transform_dirty = True self._clip_region = None self._rect = None + + # Pre-allocated arrays for polygon conversion + self._temp_points_3d = np.empty((MAX_POINTS * 2, 3), dtype=np.float32) + self._temp_proj = np.empty((3, MAX_POINTS * 2), dtype=np.float32) + self._exp_gradient = { 'start': (0.0, 1.0), # Bottom of path 'end': (0.0, 0.0), # Top of path @@ -140,10 +147,13 @@ class ModelRenderer: """Update positions of lead vehicles""" self._lead_vehicles = [LeadVehicle(), LeadVehicle()] leads = [radar_state.leadOne, radar_state.leadTwo] + for i, lead_data in enumerate(leads): if lead_data and lead_data.status: d_rel, y_rel, v_rel = lead_data.dRel, lead_data.yRel, lead_data.vRel idx = self._get_path_length_idx(path_x_array, d_rel) + + # Get z-coordinate from path at the lead vehicle position z = self._path.raw_points[idx, 2] if idx < len(self._path.raw_points) else 0.0 point = self._map_to_screen(d_rel, -y_rel, z + self._path_offset_z) if point: @@ -336,20 +346,22 @@ class ModelRenderer: return np.empty((0, 2), dtype=np.float32) # Slice points and filter non-negative x-coordinates - points = line[:max_idx + 1][line[:max_idx + 1, 0] >= 0] + points = line[:max_idx + 1] + points = points[points[:, 0] >= 0] if points.shape[0] == 0: return np.empty((0, 2), dtype=np.float32) # Create left and right 3D points in one array n_points = points.shape[0] - points_3d = np.empty((n_points * 2, 3), dtype=np.float32) + points_3d = self._temp_points_3d[:n_points * 2] points_3d[:n_points, 0] = points_3d[n_points:, 0] = points[:, 0] points_3d[:n_points, 1] = points[:, 1] - y_off points_3d[n_points:, 1] = points[:, 1] + y_off points_3d[:n_points, 2] = points_3d[n_points:, 2] = points[:, 2] + z_off # Single matrix multiplication for projections - proj = self._car_space_transform @ points_3d.T + proj = np.ascontiguousarray(self._temp_proj[:, :n_points * 2]) # Slice the pre-allocated array + np.dot(self._car_space_transform, points_3d.T, out=proj) valid_z = np.abs(proj[2]) > 1e-6 if not np.any(valid_z): return np.empty((0, 2), dtype=np.float32) @@ -390,15 +402,20 @@ class ModelRenderer: @staticmethod def _map_val(x, x0, x1, y0, y1): - x = max(x0, min(x, x1)) + x = np.clip(x, x0, x1) ra = x1 - x0 rb = y1 - y0 return (x - x0) * rb / ra + y0 if ra != 0 else y0 @staticmethod def _hsla_to_color(h, s, l, a): - r, g, b = [max(0, min(255, int(v * 255))) for v in colorsys.hls_to_rgb(h, l, s)] - return rl.Color(r, g, b, max(0, min(255, int(a * 255)))) + rgb = colorsys.hls_to_rgb(h, l, s) + return rl.Color( + int(rgb[0] * 255), + int(rgb[1] * 255), + int(rgb[2] * 255), + int(a * 255) + ) @staticmethod def _blend_colors(begin_colors, end_colors, t): From b59841329b65bc81f468dfe7a243a9c769981306 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jun 2025 00:07:19 +0800 Subject: [PATCH 409/477] system/ui: refactor AlertRenderer for improved maintainability and consistency (#35421) refactor alert --- system/ui/onroad/alert_renderer.py | 221 +++++++++++------------------ 1 file changed, 81 insertions(+), 140 deletions(-) diff --git a/system/ui/onroad/alert_renderer.py b/system/ui/onroad/alert_renderer.py index 947b19663d..e61396b89f 100644 --- a/system/ui/onroad/alert_renderer.py +++ b/system/ui/onroad/alert_renderer.py @@ -4,38 +4,42 @@ from dataclasses import dataclass from cereal import messaging, log from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.label import gui_text_box + + +ALERT_MARGIN = 40 +ALERT_PADDING = 60 +ALERT_LINE_SPACING = 45 +ALERT_BORDER_RADIUS = 30 + +ALERT_FONT_SMALL = 66 +ALERT_FONT_MEDIUM = 74 +ALERT_FONT_BIG = 88 + +SELFDRIVE_STATE_TIMEOUT = 5 # Seconds +SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds + # Constants ALERT_COLORS = { - log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 220), # Black - log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 220), # Orange - log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 220), # Red + log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 235), # Black + log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 235), # Orange + log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 235), # Red } -ALERT_HEIGHTS = { - log.SelfdriveState.AlertSize.small: 271, - log.SelfdriveState.AlertSize.mid: 420, -} - -ALERT_BORDER_RADIUS = 30 -SELFDRIVE_STATE_TIMEOUT = 5 # Seconds -SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds - @dataclass class Alert: text1: str = "" text2: str = "" - alert_type: str = "" - size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none - status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal + size: int = 0 + status: int = 0 # Pre-defined alert instances ALERT_STARTUP_PENDING = Alert( text1="openpilot Unavailable", text2="Waiting to start", - alert_type="selfdriveWaiting", size=log.SelfdriveState.AlertSize.mid, status=log.SelfdriveState.AlertStatus.normal, ) @@ -43,7 +47,6 @@ ALERT_STARTUP_PENDING = Alert( ALERT_CRITICAL_TIMEOUT = Alert( text1="TAKE CONTROL IMMEDIATELY", text2="System Unresponsive", - alert_type="selfdriveUnresponsive", size=log.SelfdriveState.AlertSize.full, status=log.SelfdriveState.AlertStatus.critical, ) @@ -51,7 +54,6 @@ ALERT_CRITICAL_TIMEOUT = Alert( ALERT_CRITICAL_REBOOT = Alert( text1="System Unresponsive", text2="Reboot Device", - alert_type="selfdriveUnresponsivePermanent", size=log.SelfdriveState.AlertSize.full, status=log.SelfdriveState.AlertStatus.critical, ) @@ -59,19 +61,13 @@ ALERT_CRITICAL_REBOOT = Alert( class AlertRenderer: def __init__(self): - """Initialize the alert renderer.""" - self.alert: Alert = Alert() # TODO: use ui_state to determine when to start self.started_frame: int = 0 self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {} - def update_state(self, sm: messaging.SubMaster) -> None: - """Update alert state based on SubMaster data.""" - self.alert = self.get_alert(sm) - - def get_alert(self, sm: messaging.SubMaster) -> Alert: + def get_alert(self, sm: messaging.SubMaster) -> Alert | None: """Generate the current alert based on selfdrive state.""" ss = sm['selfdriveState'] @@ -80,142 +76,87 @@ class AlertRenderer: return ALERT_STARTUP_PENDING # Handle selfdrive timeout - ss_missing = time.monotonic() - sm.recv_time['selfdriveState'] if TICI: + ss_missing = time.monotonic() - sm.recv_time['selfdriveState'] if ss_missing > SELFDRIVE_STATE_TIMEOUT: if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT: return ALERT_CRITICAL_TIMEOUT return ALERT_CRITICAL_REBOOT - # Return current alert from selfdrive state - return Alert( - text1=ss.alertText1, - text2=ss.alertText2, - alert_type=ss.alertType, - size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize), - status=self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus)) + # No alert if size is none + if ss.alertSize == 0: + return None + + # Return current alert + return Alert(text1=ss.alertText1, text2=ss.alertText2, size=ss.alertSize, status=ss.alertStatus) def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None: - """Render the alert within the specified rectangle.""" - self.update_state(sm) - alert_size = self._get_enum_value(self.alert.size, log.SelfdriveState.AlertSize) - if alert_size == log.SelfdriveState.AlertSize.none: + alert = self.get_alert(sm) + if not alert: return - # Calculate alert rectangle - margin = 0 if alert_size == log.SelfdriveState.AlertSize.full else 40 - height = ALERT_HEIGHTS.get(alert_size, rect.height) - alert_rect = rl.Rectangle( - rect.x + margin, - rect.y + rect.height - height + margin, - rect.width - margin * 2, - height - margin * 2, + alert_rect = self._get_alert_rect(rect, alert.size) + self._draw_background(alert_rect, alert) + + text_rect = rl.Rectangle( + alert_rect.x + ALERT_PADDING, + alert_rect.y + ALERT_PADDING, + alert_rect.width - 2 * ALERT_PADDING, + alert_rect.height - 2 * ALERT_PADDING + ) + self._draw_text(text_rect, alert) + + def _get_alert_rect(self, rect: rl.Rectangle, size: int) -> rl.Rectangle: + if size == log.SelfdriveState.AlertSize.full: + return rect + + height = (ALERT_FONT_MEDIUM + 2 * ALERT_PADDING if size == log.SelfdriveState.AlertSize.small else + ALERT_FONT_BIG + ALERT_LINE_SPACING + ALERT_FONT_SMALL + 2 * ALERT_PADDING) + + return rl.Rectangle( + rect.x + ALERT_MARGIN, + rect.y + rect.height - ALERT_MARGIN - height, + rect.width - 2 * ALERT_MARGIN, + height ) - # Draw background - alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus) - color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal]) - if alert_size != log.SelfdriveState.AlertSize.full: - roundness = ALERT_BORDER_RADIUS / (min(alert_rect.width, alert_rect.height) / 2) - rl.draw_rectangle_rounded(alert_rect, roundness, 10, color) + def _draw_background(self, rect: rl.Rectangle, alert: Alert) -> None: + color = ALERT_COLORS.get(alert.status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal]) + + if alert.size != log.SelfdriveState.AlertSize.full: + roundness = ALERT_BORDER_RADIUS / (min(rect.width, rect.height) / 2) + rl.draw_rectangle_rounded(rect, roundness, 10, color) else: - rl.draw_rectangle_rec(alert_rect, color) + rl.draw_rectangle_rec(rect, color) - # Draw text - center_x = rect.x + rect.width / 2 - center_y = alert_rect.y + alert_rect.height / 2 - self._draw_text(alert_size, alert_rect, center_x, center_y) + def _draw_text(self, rect: rl.Rectangle, alert: Alert) -> None: + if alert.size == log.SelfdriveState.AlertSize.small: + self._draw_centered(alert.text1, rect, self.font_bold, ALERT_FONT_MEDIUM) - def _draw_text( - self, alert_size: log.SelfdriveState.AlertSize, alert_rect: rl.Rectangle, center_x: float, center_y: float - ) -> None: - """Draw text based on alert size.""" - if alert_size == log.SelfdriveState.AlertSize.small: - font_size = 74 - text_width = self._measure_text(self.font_bold, self.alert.text1, font_size, 'bold').x - rl.draw_text_ex( - self.font_bold, - self.alert.text1, - rl.Vector2(center_x - text_width / 2, center_y - font_size / 2), - font_size, - 0, - rl.WHITE, - ) - elif alert_size == log.SelfdriveState.AlertSize.mid: - font_size1 = 88 - text1_width = self._measure_text(self.font_bold, self.alert.text1, font_size1, 'bold').x - rl.draw_text_ex( - self.font_bold, - self.alert.text1, - rl.Vector2(center_x - text1_width / 2, center_y - 125), - font_size1, - 0, - rl.WHITE, - ) - font_size2 = 66 - text2_width = self._measure_text(self.font_regular, self.alert.text2, font_size2, 'regular').x - rl.draw_text_ex( - self.font_regular, - self.alert.text2, - rl.Vector2(center_x - text2_width / 2, center_y + 21), - font_size2, - 0, - rl.WHITE, - ) - elif alert_size == log.SelfdriveState.AlertSize.full: - is_long = len(self.alert.text1) > 15 + elif alert.size == log.SelfdriveState.AlertSize.mid: + self._draw_centered(alert.text1, rect, self.font_bold, ALERT_FONT_BIG, center_y=False) + rect.y += ALERT_FONT_BIG + ALERT_LINE_SPACING + self._draw_centered(alert.text2, rect, self.font_regular, ALERT_FONT_SMALL, center_y=False) + + else: + is_long = len(alert.text1) > 15 font_size1 = 132 if is_long else 177 - text1_y = alert_rect.y + (240 if is_long else 270) - wrapped_text1 = self._wrap_text(self.alert.text1, alert_rect.width - 100, font_size1, self.font_bold) - for i, line in enumerate(wrapped_text1): - line_width = self._measure_text(self.font_bold, line, font_size1, 'bold').x - rl.draw_text_ex( - self.font_bold, - line, - rl.Vector2(center_x - line_width / 2, text1_y + i * font_size1), - font_size1, - 0, - rl.WHITE, - ) - font_size2 = 88 - text2_y = alert_rect.y + alert_rect.height - (361 if is_long else 420) - wrapped_text2 = self._wrap_text(self.alert.text2, alert_rect.width - 100, font_size2, self.font_regular) - for i, line in enumerate(wrapped_text2): - line_width = self._measure_text(self.font_regular, line, font_size2, 'regular').x - rl.draw_text_ex( - self.font_regular, - line, - rl.Vector2(center_x - line_width / 2, text2_y + i * font_size2), - font_size2, - 0, - rl.WHITE, - ) + align_ment = rl.GuiTextAlignment.TEXT_ALIGN_CENTER + vertical_align = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE + text_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height // 2) - def _wrap_text(self, text: str, max_width: float, font_size: int, font: rl.Font) -> list[str]: - """Wrap text to fit within max width.""" - words = text.split() - lines = [] - current_line = "" - for word in words: - test_line = f"{current_line} {word}" if current_line else word - if self._measure_text(font, test_line, font_size, 'bold' if font == self.font_bold else 'regular').x <= max_width: - current_line = test_line - else: - if current_line: - lines.append(current_line) - current_line = word - if current_line: - lines.append(current_line) - return lines + gui_text_box(text_rect, alert.text1, font_size1, alignment=align_ment, alignment_vertical=vertical_align, font_weight=FontWeight.BOLD) + text_rect.y = rect.y + rect.height // 2 + gui_text_box(text_rect, alert.text2, ALERT_FONT_BIG, alignment=align_ment) def _measure_text(self, font: rl.Font, text: str, font_size: int, font_type: str) -> rl.Vector2: - """Measure text dimensions with caching.""" key = (text, font_size, font_type) if key not in self.font_metrics_cache: self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) return self.font_metrics_cache[key] - @staticmethod - def _get_enum_value(enum_value, enum_type: type): - """Safely convert capnp enum to Python enum value.""" - return enum_value.raw if hasattr(enum_value, 'raw') else enum_value + def _draw_centered(self, text, rect, font, font_size, center_y=True, color=rl.WHITE) -> None: + text_size = self._measure_text(font, text, font_size, 'bold' if font == self.font_bold else 'regular') + x = rect.x + (rect.width - text_size.x) / 2 + y = rect.y + ((rect.height - text_size.y) / 2 if center_y else 0) + rl.draw_text_ex(font, text, rl.Vector2(x, y), font_size, 0, color) From d46066225d189de79c3c980bacfb463a29a7a725 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jun 2025 01:18:44 +0800 Subject: [PATCH 410/477] system/ui: add centralized UIState singleton for global state management (#35397) * add centralized UIState singleton for global state management * safely import ui_state * merge master * merge master --- system/ui/lib/application.py | 14 +++ system/ui/lib/ui_state.py | 129 ++++++++++++++++++++++++ system/ui/onroad/alert_renderer.py | 5 +- system/ui/onroad/augmented_road_view.py | 47 ++++----- system/ui/onroad/driver_state.py | 8 +- system/ui/onroad/hud_renderer.py | 36 +++---- system/ui/onroad/model_renderer.py | 4 +- 7 files changed, 183 insertions(+), 60 deletions(-) create mode 100644 system/ui/lib/ui_state.py diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a8b55b9e79..2e3553038a 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -36,6 +36,16 @@ class FontWeight(IntEnum): BLACK = 8 +def _get_ui_state(): + """Safely import and return ui_state, or None if unavailable.""" + try: + from openpilot.system.ui.lib.ui_state import ui_state + + return ui_state + except ImportError: + return None + + class GuiApplication: def __init__(self, width: int, height: int): self._fonts: dict[FontWeight, rl.Font] = {} @@ -139,6 +149,7 @@ class GuiApplication: rl.close_window() def render(self): + ui_state = _get_ui_state() try: while not (self._window_close_requested or rl.window_should_close()): if self._render_texture: @@ -148,6 +159,9 @@ class GuiApplication: rl.begin_drawing() rl.clear_background(rl.BLACK) + if ui_state is not None: + ui_state.update() + yield if self._render_texture: diff --git a/system/ui/lib/ui_state.py b/system/ui/lib/ui_state.py new file mode 100644 index 0000000000..d9a8c40597 --- /dev/null +++ b/system/ui/lib/ui_state.py @@ -0,0 +1,129 @@ +import pyray as rl +from enum import Enum +from cereal import messaging, log +from openpilot.common.params import Params, UnknownKeyName + + +UI_BORDER_SIZE = 30 + + +class UIStatus(Enum): + DISENGAGED = "disengaged" + ENGAGED = "engaged" + OVERRIDE = "override" + + +class UIState: + _instance: 'UIState | None' = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance._initialize() + return cls._instance + + def _initialize(self): + self.params = Params() + self.sm = messaging.SubMaster( + [ + "modelV2", + "controlsState", + "liveCalibration", + "radarState", + "deviceState", + "pandaStates", + "carParams", + "driverMonitoringState", + "carState", + "driverStateV2", + "roadCameraState", + "wideRoadCameraState", + "managerState", + "selfdriveState", + "longitudinalPlan", + ] + ) + + # UI Status tracking + self.status: UIStatus = UIStatus.DISENGAGED + self.started_frame: int = 0 + self._engaged_prev: bool = False + self._started_prev: bool = False + + # Core state variables + self.is_metric: bool = self.params.get_bool("IsMetric") + self.started: bool = False + self.ignition: bool = False + self.panda_type: log.PandaState.PandaType = log.PandaState.PandaType.unknown + self.personality: log.LongitudinalPersonality = log.LongitudinalPersonality.standard + self.light_sensor: float = -1.0 + + self._update_params() + + @property + def engaged(self) -> bool: + return self.started and self.sm["selfdriveState"].enabled + + def update(self) -> None: + self.sm.update(0) + self._update_state() + self._update_status() + + def _update_state(self) -> None: + # Handle panda states updates + if self.sm.updated["pandaStates"]: + panda_states = self.sm["pandaStates"] + + if len(panda_states) > 0: + # Get panda type from first panda + self.panda_type = panda_states[0].pandaType + # Check ignition status across all pandas + if self.panda_type != log.PandaState.PandaType.unknown: + self.ignition = any(state.ignitionLine or state.ignitionCan for state in panda_states) + elif self.sm.frame - self.sm.recv_frame["pandaStates"] > 5 * rl.get_fps(): + self.panda_type = log.PandaState.PandaType.unknown + + # Handle wide road camera state updates + if self.sm.updated["wideRoadCameraState"]: + cam_state = self.sm["wideRoadCameraState"] + + # Scale factor based on sensor type + scale = 6.0 if cam_state.sensor == 'ar0231' else 1.0 + self.light_sensor = max(100.0 - scale * cam_state.exposureValPercent, 0.0) + elif not self.sm.alive["wideRoadCameraState"] or not self.sm.valid["wideRoadCameraState"]: + self.light_sensor = -1 + + # Update started state + self.started = self.sm["deviceState"].started and self.ignition + + def _update_status(self) -> None: + if self.started and self.sm.updated["selfdriveState"]: + ss = self.sm["selfdriveState"] + state = ss.state + + if state in (log.SelfdriveState.OpenpilotState.preEnabled, log.SelfdriveState.OpenpilotState.overriding): + self.status = UIStatus.OVERRIDE + else: + self.status = UIStatus.ENGAGED if ss.enabled else UIStatus.DISENGAGED + + # Check for engagement state changes + if self.engaged != self._engaged_prev: + self._engaged_prev = self.engaged + + # Handle onroad/offroad transition + if self.started != self._started_prev or self.sm.frame == 1: + if self.started: + self.status = UIStatus.DISENGAGED + self.started_frame = self.sm.frame + + self._started_prev = self.started + + def _update_params(self) -> None: + try: + self.is_metric = self.params.get_bool("IsMetric") + except UnknownKeyName: + self.is_metric = False + + +# Global instance +ui_state = UIState() diff --git a/system/ui/onroad/alert_renderer.py b/system/ui/onroad/alert_renderer.py index e61396b89f..5bdfebb18c 100644 --- a/system/ui/onroad/alert_renderer.py +++ b/system/ui/onroad/alert_renderer.py @@ -5,6 +5,7 @@ from cereal import messaging, log from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_text_box +from openpilot.system.ui.lib.ui_state import ui_state ALERT_MARGIN = 40 @@ -61,8 +62,6 @@ ALERT_CRITICAL_REBOOT = Alert( class AlertRenderer: def __init__(self): - # TODO: use ui_state to determine when to start - self.started_frame: int = 0 self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {} @@ -72,7 +71,7 @@ class AlertRenderer: ss = sm['selfdriveState'] # Check if waiting to start - if sm.recv_frame['selfdriveState'] < self.started_frame: + if sm.recv_frame['selfdriveState'] < ui_state.started_frame: return ALERT_STARTUP_PENDING # Handle selfdrive timeout diff --git a/system/ui/onroad/augmented_road_view.py b/system/ui/onroad/augmented_road_view.py index 053307bb69..63404f3f9e 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/system/ui/onroad/augmented_road_view.py @@ -1,9 +1,9 @@ import numpy as np import pyray as rl -from enum import Enum -from cereal import messaging, log +from cereal import log from msgq.visionipc import VisionStreamType +from openpilot.system.ui.lib.ui_state import ui_state, UIStatus, UI_BORDER_SIZE from openpilot.system.ui.onroad.alert_renderer import AlertRenderer from openpilot.system.ui.onroad.driver_state import DriverStateRenderer from openpilot.system.ui.onroad.hud_renderer import HudRenderer @@ -17,19 +17,18 @@ from openpilot.common.transformations.orientation import rot_from_euler OpState = log.SelfdriveState.OpenpilotState CALIBRATED = log.LiveCalibrationData.Status.calibrated DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"] -UI_BORDER_SIZE = 30 -class BorderStatus(Enum): - DISENGAGED = rl.Color(0x17, 0x33, 0x49, 0xc8) # Blue for disengaged state - OVERRIDE = rl.Color(0x91, 0x9b, 0x95, 0xf1) # Gray for override state - ENGAGED = rl.Color(0x17, 0x86, 0x44, 0xf1) # Green for engaged state +BORDER_COLORS = { + UIStatus.DISENGAGED: rl.Color(0x17, 0x33, 0x49, 0xC8), # Blue for disengaged state + UIStatus.OVERRIDE: rl.Color(0x91, 0x9B, 0x95, 0xF1), # Gray for override state + UIStatus.ENGAGED: rl.Color(0x17, 0x86, 0x44, 0xF1), # Green for engaged state +} class AugmentedRoadView(CameraView): - def __init__(self, sm: messaging.SubMaster, stream_type: VisionStreamType): + def __init__(self, stream_type: VisionStreamType): super().__init__("camerad", stream_type) - self.sm = sm self.device_camera: DeviceCameraConfig | None = None self.view_from_calib = view_frame_from_device_frame.copy() self.view_from_wide_calib = view_frame_from_device_frame.copy() @@ -72,10 +71,10 @@ class AugmentedRoadView(CameraView): super().render(rect) # Draw all UI overlays - self.model_renderer.draw(self._content_rect, self.sm) - self._hud_renderer.draw(self._content_rect, self.sm) - self.alert_renderer.draw(self._content_rect, self.sm) - self.driver_state_renderer.draw(self._content_rect, self.sm) + self.model_renderer.draw(self._content_rect, ui_state.sm) + self._hud_renderer.draw(self._content_rect, ui_state.sm) + self.alert_renderer.draw(self._content_rect, ui_state.sm) + self.driver_state_renderer.draw(self._content_rect, ui_state.sm) # Custom UI extension point - add custom overlays here # Use self._content_rect for positioning within camera bounds @@ -84,18 +83,12 @@ class AugmentedRoadView(CameraView): rl.end_scissor_mode() def _draw_border(self, rect: rl.Rectangle): - state = self.sm["selfdriveState"] - if state.state in (OpState.preEnabled, OpState.overriding): - status = BorderStatus.OVERRIDE - elif state.enabled: - status = BorderStatus.ENGAGED - else: - status = BorderStatus.DISENGAGED - - rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, status.value) + border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED]) + rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, border_color) def _update_calibration(self): # Update device camera if not already set + sm = ui_state.sm if not self.device_camera and sm.seen['roadCameraState'] and sm.seen['deviceState']: self.device_camera = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] @@ -103,7 +96,7 @@ class AugmentedRoadView(CameraView): if not (sm.updated["liveCalibration"] and sm.valid['liveCalibration']): return - calib = self.sm['liveCalibration'] + calib = sm['liveCalibration'] if len(calib.rpyCalib) != 3 or calib.calStatus != CALIBRATED: return @@ -118,7 +111,7 @@ class AugmentedRoadView(CameraView): def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray: # Check if we can use cached matrix - calib_time = self.sm.recv_frame['liveCalibration'] + calib_time = ui_state.sm.recv_frame['liveCalibration'] current_dims = (self._content_rect.width, self._content_rect.height) if (self._last_calib_time == calib_time and self._last_rect_dims == current_dims and @@ -178,14 +171,10 @@ class AugmentedRoadView(CameraView): if __name__ == "__main__": gui_app.init_window("OnRoad Camera View") - sm = messaging.SubMaster(["modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", - "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", - "roadCameraState", "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan"]) - road_camera_view = AugmentedRoadView(sm, VisionStreamType.VISION_STREAM_ROAD) + road_camera_view = AugmentedRoadView(VisionStreamType.VISION_STREAM_ROAD) print("***press space to switch camera view***") try: for _ in gui_app.render(): - sm.update(0) if rl.is_key_released(rl.KeyboardKey.KEY_SPACE): is_wide = road_camera_view.stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD road_camera_view.switch_stream(VisionStreamType.VISION_STREAM_ROAD if is_wide else VisionStreamType.VISION_STREAM_WIDE_ROAD) diff --git a/system/ui/onroad/driver_state.py b/system/ui/onroad/driver_state.py index 7527fa6255..8017021ee5 100644 --- a/system/ui/onroad/driver_state.py +++ b/system/ui/onroad/driver_state.py @@ -2,6 +2,8 @@ import numpy as np import pyray as rl from dataclasses import dataclass from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.ui_state import ui_state, UI_BORDER_SIZE + # Default 3D coordinates for face keypoints as a NumPy array DEFAULT_FACE_KPTS_3D = np.array([ @@ -17,7 +19,6 @@ DEFAULT_FACE_KPTS_3D = np.array([ ], dtype=np.float32) # UI constants -UI_BORDER_SIZE = 30 BTN_SIZE = 192 IMG_SIZE = 144 ARC_LENGTH = 133 @@ -95,8 +96,7 @@ class DriverStateRenderer: rl.draw_spline_linear(self.face_lines, len(self.face_lines), 5.2, self.white_color) # Set arc color based on engaged state - engaged = True - self.arc_color = self.engaged_color if engaged else self.disengaged_color + self.arc_color = self.engaged_color if ui_state.engaged else self.disengaged_color self.arc_color.a = int(0.4 * 255 * (1.0 - self.dm_fade_state)) # Fade out when inactive # Draw arcs @@ -107,7 +107,7 @@ class DriverStateRenderer: def _is_visible(self, sm): """Check if the visualization should be rendered.""" - return (sm.seen['driverStateV2'] and + return (sm.recv_frame['driverStateV2'] > ui_state.started_frame and sm.seen['driverMonitoringState'] and sm['selfdriveState'].alertSize == 0) diff --git a/system/ui/onroad/hud_renderer.py b/system/ui/onroad/hud_renderer.py index ee182daef8..3ba2cb2280 100644 --- a/system/ui/onroad/hud_renderer.py +++ b/system/ui/onroad/hud_renderer.py @@ -1,9 +1,9 @@ import pyray as rl from dataclasses import dataclass from cereal.messaging import SubMaster +from openpilot.system.ui.lib.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.common.conversions import Conversions as CV -from enum import IntEnum # Constants SET_SPEED_NA = 255 @@ -53,17 +53,9 @@ FONT_SIZES = FontSizes() COLORS = Colors() -class HudStatus(IntEnum): - DISENGAGED = 0 - OVERRIDE = 1 - ENGAGED = 2 - - class HudRenderer: def __init__(self): """Initialize the HUD renderer.""" - self.is_metric: bool = False - self.status: HudStatus = HudStatus.DISENGAGED self.is_cruise_set: bool = False self.is_cruise_available: bool = False self.set_speed: float = SET_SPEED_NA @@ -77,10 +69,7 @@ class HudRenderer: def _update_state(self, sm: SubMaster) -> None: """Update HUD state based on car state and controls state.""" - self.is_metric = True - self.status = HudStatus.DISENGAGED - - if not sm.valid['carState']: + if sm.recv_frame["carState"] < ui_state.started_frame: self.is_cruise_set = False self.set_speed = SET_SPEED_NA self.speed = 0.0 @@ -96,13 +85,13 @@ class HudRenderer: self.is_cruise_set = 0 < self.set_speed < SET_SPEED_NA self.is_cruise_available = self.set_speed != -1 - if self.is_cruise_set and not self.is_metric: + if self.is_cruise_set and not ui_state.is_metric: self.set_speed *= KM_TO_MILE v_ego_cluster = car_state.vEgoCluster self.v_ego_cluster_seen = self.v_ego_cluster_seen or v_ego_cluster != 0.0 v_ego = v_ego_cluster if self.v_ego_cluster_seen else car_state.vEgo - speed_conversion = CV.MS_TO_KPH if self.is_metric else CV.MS_TO_MPH + speed_conversion = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH self.speed = max(0.0, v_ego * speed_conversion) def draw(self, rect: rl.Rectangle, sm: SubMaster) -> None: @@ -125,7 +114,7 @@ class HudRenderer: def _draw_set_speed(self, rect: rl.Rectangle) -> None: """Draw the MAX speed indicator box.""" - set_speed_width = UI_CONFIG.set_speed_width_metric if self.is_metric else UI_CONFIG.set_speed_width_imperial + set_speed_width = UI_CONFIG.set_speed_width_metric if ui_state.is_metric else UI_CONFIG.set_speed_width_imperial x = rect.x + 60 + (UI_CONFIG.set_speed_width_imperial - set_speed_width) // 2 y = rect.y + 45 @@ -137,11 +126,12 @@ class HudRenderer: set_speed_color = COLORS.dark_grey if self.is_cruise_set: set_speed_color = COLORS.white - max_color = { - HudStatus.DISENGAGED: COLORS.disengaged, - HudStatus.OVERRIDE: COLORS.override, - HudStatus.ENGAGED: COLORS.engaged, - }.get(self.status, COLORS.grey) + if ui_state.status == UIStatus.ENGAGED: + max_color = COLORS.engaged + elif ui_state.status == UIStatus.DISENGAGED: + max_color = COLORS.disengaged + elif ui_state.status == UIStatus.OVERRIDE: + max_color = COLORS.override max_text = "MAX" max_text_width = self._measure_text(max_text, self._font_semi_bold, FONT_SIZES.max_speed, 'semi_bold').x @@ -172,7 +162,7 @@ class HudRenderer: speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2) rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.white) - unit_text = "km/h" if self.is_metric else "mph" + unit_text = "km/h" if ui_state.is_metric else "mph" unit_text_size = self._measure_text(unit_text, self._font_medium, FONT_SIZES.speed_unit, 'medium') unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent) @@ -183,7 +173,7 @@ class HudRenderer: center_y = int(rect.y + UI_CONFIG.border_size + UI_CONFIG.button_size / 2) rl.draw_circle(center_x, center_y, UI_CONFIG.button_size / 2, COLORS.black_translucent) - opacity = 0.7 if self.status == HudStatus.DISENGAGED else 1.0 + opacity = 0.7 if ui_state.status == UIStatus.DISENGAGED else 1.0 img_pos = rl.Vector2(center_x - self._wheel_texture.width / 2, center_y - self._wheel_texture.height / 2) rl.draw_texture_v(self._wheel_texture, img_pos, rl.Color(255, 255, 255, int(255 * opacity))) diff --git a/system/ui/onroad/model_renderer.py b/system/ui/onroad/model_renderer.py index 44bd3f72ca..eedf10cc4f 100644 --- a/system/ui/onroad/model_renderer.py +++ b/system/ui/onroad/model_renderer.py @@ -5,6 +5,7 @@ from cereal import messaging, car from dataclasses import dataclass, field from openpilot.common.params import Params from openpilot.system.ui.lib.application import DEFAULT_FPS +from openpilot.system.ui.lib.ui_state import ui_state from openpilot.system.ui.lib.shader_polygon import draw_polygon @@ -86,7 +87,8 @@ class ModelRenderer: def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster): # Check if data is up-to-date - if not sm.valid['modelV2'] or not sm.valid['liveCalibration']: + if (sm.recv_frame["liveCalibration"] < ui_state.started_frame or + sm.recv_frame["modelV2"] < ui_state.started_frame): return # Set up clipping region From bb36b6687c945468411b4c5686e9e42cf93ad820 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 11:44:00 -0700 Subject: [PATCH 411/477] [bot] Update translations (#35424) Update translations Co-authored-by: Vehicle Researcher --- selfdrive/ui/translations/main_ar.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_de.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_es.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_fr.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_ja.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_ko.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_pt-BR.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_th.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_tr.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_zh-CHS.ts | 20 ++++++++++++++++---- selfdrive/ui/translations/main_zh-CHT.ts | 20 ++++++++++++++++---- 11 files changed, 176 insertions(+), 44 deletions(-) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 0c3b934dde..7ebb1987b2 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -306,6 +306,14 @@ PAIR إقران + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1040,10 +1048,6 @@ This may take up to a minute. Enable openpilot تمكين openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - استخدم نظام openpilot من أجل الضبط التكيفي للسرعة والحفاظ على مساعدة السائق للبقاء في المسار. انتباهك مطلوب في جميع الأوقات مع استخدام هذه الميزة. يعمل هذا التغيير في الإعدادات عند إيقاف تشغيل السيارة. - Enable Lane Departure Warnings قم بتمكين تحذيرات مغادرة المسار @@ -1144,6 +1148,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. تمكين مراقبة السائق حتى عندما لا يكون نظام OpenPilot مُفعّلاً. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index db0fe97036..386b70476e 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -306,6 +306,14 @@ PAIR KOPPELN + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1022,10 +1030,6 @@ Dies kann bis zu einer Minute dauern. Enable openpilot Openpilot aktivieren - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Benutze das Openpilot System als adaptiven Tempomaten und Spurhalteassistenten. Deine Aufmerksamkeit ist jederzeit erforderlich, um diese Funktion zu nutzen. Diese Einstellung wird übernommen, wenn das Auto aus ist. - Enable Lane Departure Warnings Spurverlassenswarnungen aktivieren @@ -1126,6 +1130,14 @@ Dies kann bis zu einer Minute dauern. Enable driver monitoring even when openpilot is not engaged. Fahrerüberwachung auch aktivieren, wenn openpilot nicht aktiv ist. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index c4ead7ca9d..ba35d2110b 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -306,6 +306,14 @@ Disengage to Power Off Desactivar para apagar + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1020,10 +1028,6 @@ Esto puede tardar un minuto. Enable openpilot Activar openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Utilice el sistema openpilot para acceder a un autocrucero adaptativo y asistencia al conductor para mantenerse en el carril. Se requiere su atención en todo momento para utilizar esta función. Cambiar esta configuración solo tendrá efecto con el auto apagado. - Experimental Mode Modo Experimental @@ -1124,6 +1128,14 @@ Esto puede tardar un minuto. Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode. Activar el control longitudinal (fase experimental) para permitir el modo Experimental. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index 74849bd6fc..2b7dd4a5e8 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -306,6 +306,14 @@ PAIR ASSOCIER + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1018,10 +1026,6 @@ Cela peut prendre jusqu'à une minute. Enable openpilot Activer openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Utilisez le système openpilot pour le régulateur de vitesse adaptatif et l'assistance au maintien de voie. Votre attention est requise en permanence pour utiliser cette fonctionnalité. La modification de ce paramètre prend effet lorsque la voiture est éteinte. - Experimental Mode Mode expérimental @@ -1122,6 +1126,14 @@ Cela peut prendre jusqu'à une minute. Enable driver monitoring even when openpilot is not engaged. Activer la surveillance conducteur lorsque openpilot n'est pas actif. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 169df93c33..821fa35752 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -306,6 +306,14 @@ PAIR OK + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1015,10 +1023,6 @@ This may take up to a minute. Enable openpilot openpilotを有効化 - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - openpilotによるアダプティブクルーズコントロールとレーンキープアシストを利用します。この機能を利用する際は常に前方への注意が必要です。この設定を変更は車の電源が必要です。 - Enable Lane Departure Warnings 車線逸脱警報の有効化 @@ -1119,6 +1123,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. openpilotが作動していない場合でも運転者モニタリングを有効にする。 + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index dea01b44ee..0d91b06049 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -306,6 +306,14 @@ PAIR 동기화 + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1015,10 +1023,6 @@ This may take up to a minute. Enable openpilot 오픈파일럿 사용 - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 오픈파일럿 시스템을 사용하여 어댑티브 크루즈 컨트롤과 차로 유지 보조 기능을 활용하십시오. 이 기능을 사용할 때에는 항상 주의를 기울여야 합니다. 설정 변경은 차량을 재시동해야 적용됩니다. - Enable Lane Departure Warnings 차선 이탈 경고 활성화 @@ -1119,6 +1123,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 오픈파일럿이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 3182370f42..e25f345ae7 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -306,6 +306,14 @@ PAIR PAREAR + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1020,10 +1028,6 @@ Isso pode levar até um minuto. Enable openpilot Ativar openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. A alteração desta configuração tem efeito quando o carro é desligado. - Enable Lane Departure Warnings Ativar Avisos de Saída de Faixa @@ -1124,6 +1128,14 @@ Isso pode levar até um minuto. Enable driver monitoring even when openpilot is not engaged. Habilite o monitoramento do motorista mesmo quando o openpilot não estiver acionado. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index c587cfbd12..034b996606 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -306,6 +306,14 @@ PAIR จับคู่ + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1015,10 +1023,6 @@ This may take up to a minute. Enable openpilot เปิดใช้งาน openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - ใช้ระบบ openpilot สำหรับระบบควบคุมความเร็วอัตโนมัติ และระบบช่วยควบคุมรถให้อยู่ในเลน คุณจำเป็นต้องให้ความสนใจตลอดเวลาที่ใช้คุณสมบัตินี้ การเปลี่ยนการตั้งค่านี้จะมีผลเมื่อคุณดับเครื่องยนต์ - Enable Lane Departure Warnings เปิดใช้งานการเตือนการออกนอกเลน @@ -1119,6 +1123,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. เปิดใช้งานการเฝ้าระวังผู้ขับขี่แม้เมื่อ openpilot ไม่ได้เข้าควบคุมอยู่ + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 62de11145a..cd30fae20f 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -306,6 +306,14 @@ PAIR + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1011,10 +1019,6 @@ This may take up to a minute. Enable openpilot openpilot'u aktifleştir - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - Ayarlanabilir hız sabitleyici ve şeritte kalma yardımı için openpilot sistemini kullanın. Bu özelliği kullanırken her zaman dikkatli olmanız gerekiyor. Bu ayarın değiştirilmesi için araç kapatılıp açılması gerekiyor. - Enable Lane Departure Warnings Şerit ihlali uyarı alın @@ -1115,6 +1119,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 26537d3f64..af1f940cf5 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -306,6 +306,14 @@ PAIR 配对 + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1015,10 +1023,6 @@ This may take up to a minute. Enable openpilot 启用openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 使用openpilot进行自适应巡航和车道保持辅助。使用此功能时您必须时刻保持注意力。该设置的更改在熄火时生效。 - Enable Lane Departure Warnings 启用车道偏离警告 @@ -1119,6 +1123,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 即使在openpilot未激活时也启用驾驶员监控。 + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 160c293072..534639e09b 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -306,6 +306,14 @@ PAIR 配對 + + Disengage to Reset Calibration + + + + Resetting calibration will restart openpilot if the car is powered on. + + DriverViewWindow @@ -1015,10 +1023,6 @@ This may take up to a minute. Enable openpilot 啟用 openpilot - - Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 使用 openpilot 的主動式巡航和車道保持功能,開啟後您需要持續集中注意力,設定變更在重新啟動車輛後生效。 - Enable Lane Departure Warnings 啟用車道偏離警告 @@ -1119,6 +1123,14 @@ This may take up to a minute. Enable driver monitoring even when openpilot is not engaged. 即使在openpilot未激活時也啟用駕駛監控。 + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. + + + + Changing this setting will restart openpilot if the car is powered on. + + Updater From 29afd667ccf3c72a3a2228b0608b9554e5dee1c4 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jun 2025 04:27:17 +0800 Subject: [PATCH 412/477] move onroad/driving stuff from system/ui to selfdrive/ui (#35425) * mv system/ui/onroad->selfdrive/ui/onroad * mv ui_state * fix import path * fix imports * mv cameraview * remove from application --- selfdrive/ui/onroad/__init__.py | 0 .../ui/onroad/alert_renderer.py | 2 +- .../ui/onroad/augmented_road_view.py | 13 +++++++------ .../ui/onroad}/cameraview.py | 0 .../ui/onroad}/driver_camera_view.py | 4 ++-- {system => selfdrive}/ui/onroad/driver_state.py | 2 +- {system => selfdrive}/ui/onroad/hud_renderer.py | 2 +- .../ui/onroad/model_renderer.py | 2 +- {system/ui/lib => selfdrive/ui}/ui_state.py | 0 system/ui/lib/application.py | 16 +--------------- 10 files changed, 14 insertions(+), 27 deletions(-) create mode 100644 selfdrive/ui/onroad/__init__.py rename {system => selfdrive}/ui/onroad/alert_renderer.py (99%) rename {system => selfdrive}/ui/onroad/augmented_road_view.py (93%) rename {system/ui/widgets => selfdrive/ui/onroad}/cameraview.py (100%) rename {system/ui/widgets => selfdrive/ui/onroad}/driver_camera_view.py (95%) rename {system => selfdrive}/ui/onroad/driver_state.py (99%) rename {system => selfdrive}/ui/onroad/hud_renderer.py (99%) rename {system => selfdrive}/ui/onroad/model_renderer.py (99%) rename {system/ui/lib => selfdrive/ui}/ui_state.py (100%) diff --git a/selfdrive/ui/onroad/__init__.py b/selfdrive/ui/onroad/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/system/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py similarity index 99% rename from system/ui/onroad/alert_renderer.py rename to selfdrive/ui/onroad/alert_renderer.py index 5bdfebb18c..cc1b49a6a4 100644 --- a/system/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -5,7 +5,7 @@ from cereal import messaging, log from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_text_box -from openpilot.system.ui.lib.ui_state import ui_state +from openpilot.selfdrive.ui.ui_state import ui_state ALERT_MARGIN = 40 diff --git a/system/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py similarity index 93% rename from system/ui/onroad/augmented_road_view.py rename to selfdrive/ui/onroad/augmented_road_view.py index 63404f3f9e..9a7d88e504 100644 --- a/system/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -3,12 +3,12 @@ import pyray as rl from cereal import log from msgq.visionipc import VisionStreamType -from openpilot.system.ui.lib.ui_state import ui_state, UIStatus, UI_BORDER_SIZE -from openpilot.system.ui.onroad.alert_renderer import AlertRenderer -from openpilot.system.ui.onroad.driver_state import DriverStateRenderer -from openpilot.system.ui.onroad.hud_renderer import HudRenderer -from openpilot.system.ui.onroad.model_renderer import ModelRenderer -from openpilot.system.ui.widgets.cameraview import CameraView +from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus, UI_BORDER_SIZE +from openpilot.selfdrive.ui.onroad.alert_renderer import AlertRenderer +from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer +from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer +from openpilot.selfdrive.ui.onroad.model_renderer import ModelRenderer +from openpilot.selfdrive.ui.onroad.cameraview import CameraView from openpilot.system.ui.lib.application import gui_app from openpilot.common.transformations.camera import DEVICE_CAMERAS, DeviceCameraConfig, view_frame_from_device_frame from openpilot.common.transformations.orientation import rot_from_euler @@ -175,6 +175,7 @@ if __name__ == "__main__": print("***press space to switch camera view***") try: for _ in gui_app.render(): + ui_state.update() if rl.is_key_released(rl.KeyboardKey.KEY_SPACE): is_wide = road_camera_view.stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD road_camera_view.switch_stream(VisionStreamType.VISION_STREAM_ROAD if is_wide else VisionStreamType.VISION_STREAM_WIDE_ROAD) diff --git a/system/ui/widgets/cameraview.py b/selfdrive/ui/onroad/cameraview.py similarity index 100% rename from system/ui/widgets/cameraview.py rename to selfdrive/ui/onroad/cameraview.py diff --git a/system/ui/widgets/driver_camera_view.py b/selfdrive/ui/onroad/driver_camera_view.py similarity index 95% rename from system/ui/widgets/driver_camera_view.py rename to selfdrive/ui/onroad/driver_camera_view.py index 56dcbf2b9e..2f9ff2ca7a 100644 --- a/system/ui/widgets/driver_camera_view.py +++ b/selfdrive/ui/onroad/driver_camera_view.py @@ -1,11 +1,11 @@ import numpy as np import pyray as rl from cereal import messaging -from openpilot.system.ui.widgets.cameraview import CameraView from msgq.visionipc import VisionStreamType +from openpilot.selfdrive.ui.onroad.cameraview import CameraView +from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_label -from openpilot.system.ui.onroad.driver_state import DriverStateRenderer class DriverCameraView(CameraView): diff --git a/system/ui/onroad/driver_state.py b/selfdrive/ui/onroad/driver_state.py similarity index 99% rename from system/ui/onroad/driver_state.py rename to selfdrive/ui/onroad/driver_state.py index 8017021ee5..8e950f514e 100644 --- a/system/ui/onroad/driver_state.py +++ b/selfdrive/ui/onroad/driver_state.py @@ -1,8 +1,8 @@ import numpy as np import pyray as rl from dataclasses import dataclass +from openpilot.selfdrive.ui.ui_state import ui_state, UI_BORDER_SIZE from openpilot.system.ui.lib.application import gui_app -from openpilot.system.ui.lib.ui_state import ui_state, UI_BORDER_SIZE # Default 3D coordinates for face keypoints as a NumPy array diff --git a/system/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py similarity index 99% rename from system/ui/onroad/hud_renderer.py rename to selfdrive/ui/onroad/hud_renderer.py index 3ba2cb2280..ed0b7cc974 100644 --- a/system/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -1,7 +1,7 @@ import pyray as rl from dataclasses import dataclass from cereal.messaging import SubMaster -from openpilot.system.ui.lib.ui_state import ui_state, UIStatus +from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.common.conversions import Conversions as CV diff --git a/system/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py similarity index 99% rename from system/ui/onroad/model_renderer.py rename to selfdrive/ui/onroad/model_renderer.py index eedf10cc4f..38951fb504 100644 --- a/system/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -4,8 +4,8 @@ import pyray as rl from cereal import messaging, car from dataclasses import dataclass, field from openpilot.common.params import Params +from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import DEFAULT_FPS -from openpilot.system.ui.lib.ui_state import ui_state from openpilot.system.ui.lib.shader_polygon import draw_polygon diff --git a/system/ui/lib/ui_state.py b/selfdrive/ui/ui_state.py similarity index 100% rename from system/ui/lib/ui_state.py rename to selfdrive/ui/ui_state.py diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 2e3553038a..fcef55d531 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -36,16 +36,6 @@ class FontWeight(IntEnum): BLACK = 8 -def _get_ui_state(): - """Safely import and return ui_state, or None if unavailable.""" - try: - from openpilot.system.ui.lib.ui_state import ui_state - - return ui_state - except ImportError: - return None - - class GuiApplication: def __init__(self, width: int, height: int): self._fonts: dict[FontWeight, rl.Font] = {} @@ -149,7 +139,6 @@ class GuiApplication: rl.close_window() def render(self): - ui_state = _get_ui_state() try: while not (self._window_close_requested or rl.window_should_close()): if self._render_texture: @@ -159,9 +148,6 @@ class GuiApplication: rl.begin_drawing() rl.clear_background(rl.BLACK) - if ui_state is not None: - ui_state.update() - yield if self._render_texture: @@ -206,7 +192,7 @@ class GuiApplication: # Create a character set from our keyboard layouts from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS - from openpilot.system.ui.onroad.hud_renderer import CRUISE_DISABLED_CHAR + from openpilot.selfdrive.ui.onroad.hud_renderer import CRUISE_DISABLED_CHAR all_chars = set() for layout in KEYBOARD_LAYOUTS.values(): all_chars.update(key for row in layout for key in row) From 1ee7b4ad0efeb166ad5d54becb5479d59283e5b0 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Mon, 2 Jun 2025 23:49:29 -0400 Subject: [PATCH 413/477] ci: higher delay between UI Preview screenshots (#978) --- selfdrive/ui/tests/test_ui/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index f8c64de769..0b9d266ad6 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -26,7 +26,7 @@ from openpilot.tools.lib.framereader import FrameReader from openpilot.tools.lib.route import Route from openpilot.tools.lib.cache import DEFAULT_CACHE_DIR -UI_DELAY = 0.1 # may be slower on CI? +UI_DELAY = 0.5 # may be slower on CI? TEST_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" STREAMS: list[tuple[VisionStreamType, CameraConfig, bytes]] = [] From 1a84b05bded44a17c526292686054f66a9749fe9 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 2 Jun 2025 20:53:11 -0700 Subject: [PATCH 414/477] lagd: make method static --- selfdrive/locationd/lagd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index 4e207b4882..a61793e223 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -303,7 +303,8 @@ class LateralLagEstimator: self.block_avg.update(delay) self.last_estimate_t = self.t - def actuator_delay(self, expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, dt: float, max_lag: float) -> tuple[float, float, float]: + @staticmethod + def actuator_delay(expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, dt: float, max_lag: float) -> tuple[float, float, float]: assert len(expected_sig) == len(actual_sig) max_lag_samples = int(max_lag / dt) padded_size = fft_next_good_size(len(expected_sig) + max_lag_samples) From 28bf362f69d7f1d2069f3b7de6feb80de4e00b32 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jun 2025 12:13:21 +0800 Subject: [PATCH 415/477] ui: add safety check to prevent ui overlay access to invalid data (#35432) check ui_state.started --- selfdrive/ui/onroad/augmented_road_view.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 9a7d88e504..f987d70c9c 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -44,6 +44,10 @@ class AugmentedRoadView(CameraView): self.driver_state_renderer = DriverStateRenderer() def render(self, rect): + # Only render when system is started to avoid invalid data access + if not ui_state.started: + return + # Update calibration before rendering self._update_calibration() From 8695134b089af6206a10a54aca40cdfa0555e6fa Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 2 Jun 2025 21:14:53 -0700 Subject: [PATCH 416/477] Faster lagd test (#35430) * disable in release * pass * one liner * saves 1s so far * clean up * fix * Revert "one liner" This reverts commit 5f419b5692565fc7ddc2abe9a40112fdc47ffe9e. Revert "pass" This reverts commit 47d260e76a9cb1310b95ea1ff83ab7f8808be96d. Revert "disable in release" This reverts commit 1782718b61891d87cbb9daa1c4c65314f505cee4. * clean up * there's more * no mocker! --- selfdrive/locationd/test/test_lagd.py | 48 +++++++++++---------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/selfdrive/locationd/test/test_lagd.py b/selfdrive/locationd/test/test_lagd.py index b805f1759d..aad8cff229 100644 --- a/selfdrive/locationd/test/test_lagd.py +++ b/selfdrive/locationd/test/test_lagd.py @@ -3,7 +3,7 @@ import numpy as np import time import pytest -from cereal import messaging, log +from cereal import messaging, log, car from openpilot.selfdrive.locationd.lagd import LateralLagEstimator, retrieve_initial_lag, masked_normalized_cross_correlation, \ BLOCK_NUM_NEEDED, BLOCK_SIZE, MIN_OKAY_WINDOW_SEC from openpilot.selfdrive.test.process_replay.migration import migrate, migrate_carParams @@ -16,11 +16,7 @@ MAX_ERR_FRAMES = 1 DT = 0.05 -def process_messages(mocker, estimator, lag_frames, n_frames, vego=20.0, rejection_threshold=0.0): - class ZeroMock(mocker.Mock): - def __getattr__(self, *args): - return 0 - +def process_messages(estimator, lag_frames, n_frames, vego=20.0, rejection_threshold=0.0): for i in range(n_frames): t = i * estimator.dt desired_la = np.cos(10 * t) * 0.1 @@ -31,19 +27,15 @@ def process_messages(mocker, estimator, lag_frames, n_frames, vego=20.0, rejecti if rejected: actual_la = desired_la - desired_cuvature = desired_la / (vego ** 2) - actual_yr = actual_la / vego + desired_cuvature = float(desired_la / (vego ** 2)) + actual_yr = float(actual_la / vego) msgs = [ - (t, "carControl", mocker.Mock(latActive=not rejected)), - (t, "carState", mocker.Mock(vEgo=vego, steeringPressed=False)), - (t, "controlsState", mocker.Mock(desiredCurvature=desired_cuvature, - lateralControlState=mocker.Mock(which=mocker.Mock(return_value='debugControlState'), debugControlState=ZeroMock()))), - (t, "livePose", mocker.Mock(orientationNED=ZeroMock(), - velocityDevice=ZeroMock(), - accelerationDevice=ZeroMock(), - angularVelocityDevice=ZeroMock(z=actual_yr, valid=True), - posenetOK=True, inputsOK=True)), - (t, "liveCalibration", mocker.Mock(rpyCalib=[0, 0, 0], calStatus=log.LiveCalibrationData.Status.calibrated)), + (t, "carControl", car.CarControl(latActive=not rejected)), + (t, "carState", car.CarState(vEgo=vego, steeringPressed=False)), + (t, "controlsState", log.ControlsState(desiredCurvature=desired_cuvature)), + (t, "livePose", log.LivePose(angularVelocityDevice=log.LivePose.XYZMeasurement(z=actual_yr, valid=True), + posenetOK=True, inputsOK=True)), + (t, "liveCalibration", log.LiveCalibrationData(rpyCalib=[0, 0, 0], calStatus=log.LiveCalibrationData.Status.calibrated)), ] for t, w, m in msgs: estimator.handle_log(t, w, m) @@ -94,8 +86,8 @@ class TestLagd: corr = masked_normalized_cross_correlation(desired_sig, actual_sig, mask, 200)[len(desired_sig) - 1:len(desired_sig) + 20] assert np.argmax(corr) in range(lag_frames - MAX_ERR_FRAMES, lag_frames + MAX_ERR_FRAMES + 1) - def test_empty_estimator(self, mocker): - mocked_CP = mocker.Mock(steerActuatorDelay=0.8) + def test_empty_estimator(self): + mocked_CP = car.CarParams(steerActuatorDelay=0.8) estimator = LateralLagEstimator(mocked_CP, DT) msg = estimator.get_msg(True) assert msg.liveDelay.status == 'unestimated' @@ -103,12 +95,12 @@ class TestLagd: assert np.allclose(msg.liveDelay.lateralDelayEstimate, estimator.initial_lag) assert msg.liveDelay.validBlocks == 0 - def test_estimator_basics(self, mocker, subtests): + def test_estimator_basics(self, subtests): for lag_frames in range(5): with subtests.test(msg=f"lag_frames={lag_frames}"): - mocked_CP = mocker.Mock(steerActuatorDelay=0.8) + mocked_CP = car.CarParams(steerActuatorDelay=0.8) estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0) - process_messages(mocker, estimator, lag_frames, int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_NUM_NEEDED * BLOCK_SIZE) + process_messages(estimator, lag_frames, int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_NUM_NEEDED * BLOCK_SIZE) msg = estimator.get_msg(True) assert msg.liveDelay.status == 'estimated' assert np.allclose(msg.liveDelay.lateralDelay, lag_frames * DT, atol=0.01) @@ -116,18 +108,18 @@ class TestLagd: assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01) assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED - def test_estimator_masking(self, mocker): - mocked_CP, lag_frames = mocker.Mock(steerActuatorDelay=0.8), random.randint(1, 19) + def test_estimator_masking(self): + mocked_CP, lag_frames = car.CarParams(steerActuatorDelay=0.8), random.randint(1, 19) estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0, min_valid_block_count=1) - process_messages(mocker, estimator, lag_frames, (int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_SIZE) * 2, rejection_threshold=0.4) + process_messages(estimator, lag_frames, (int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_SIZE) * 2, rejection_threshold=0.4) msg = estimator.get_msg(True) assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01) assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01) @pytest.mark.skipif(PC, reason="only on device") @pytest.mark.timeout(60) - def test_estimator_performance(self, mocker): - mocked_CP = mocker.Mock(steerActuatorDelay=0.8) + def test_estimator_performance(self): + mocked_CP = car.CarParams(steerActuatorDelay=0.8) estimator = LateralLagEstimator(mocked_CP, DT) ds = [] From 9568f64c574cbe703f13285cfac32cb95cbdd19a Mon Sep 17 00:00:00 2001 From: Lee Jong Mun <43285072+crwusiz@users.noreply.github.com> Date: Tue, 3 Jun 2025 13:15:08 +0900 Subject: [PATCH 417/477] Multilang: kor translation update (#35429) kor translation update --- selfdrive/ui/translations/main_ko.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index 0d91b06049..bdebdcab73 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -60,7 +60,7 @@ Cellular Metered - 데이터 요금제 + 모바일 데이터 종량제 Hidden Network @@ -84,27 +84,27 @@ Prevent large data uploads when on a metered cellular connection - + 모바일 데이터 종량제 사용 시 대용량 데이터 업로드 방지 default - + 기본 metered - + 종량제 unmetered - + 무제한 Wi-Fi Network Metered - + 제한된 Wi-Fi 네트워크 Prevent large data uploads when on a metered Wi-Fi connection - + 제한된 Wi-Fi 사용 시 대용량 데이터 업로드 방지 @@ -248,7 +248,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required. - 오픈파일럿 장치는 좌우 4°, 위로 5°, 아래로 9° 이내 각도로 장착되어야 합니다. 오픈파일럿은 지속적으로 자동 보정되며 재설정은 거의 필요하지 않습니다. + 오픈파일럿 장치는 좌우 4°, 위로 5°, 아래로 9° 이내의 각도로 장착되어야 합니다. 오픈파일럿은 지속적으로 자동 보정되며 재설정은 거의 필요하지 않습니다. Your device is pointed %1° %2 and %3° %4. @@ -308,11 +308,11 @@ Disengage to Reset Calibration - + 캘리브레이션을 재설정하려면 해제하세요 Resetting calibration will restart openpilot if the car is powered on. - + 차량이 전원이 켜진 경우 캘리브레이션 재설정이 오픈파일럿을 재시작합니다. @@ -1125,11 +1125,11 @@ This may take up to a minute. Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - + ACC 및 차선 유지 지원을 위해 오픈파일럿 시스템을 사용하십시오. 이 기능을 사용하려면 항상주의를 기울여야합니다. Changing this setting will restart openpilot if the car is powered on. - + 이 설정을 변경하면 차량이 재가동된후 오픈파일럿이 시작됩니다. From 108790870ef3d307fc5d5518d1d50f75dcaea048 Mon Sep 17 00:00:00 2001 From: Alexandre Nobuharu Sato <66435071+AlexandreSato@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:18:34 -0300 Subject: [PATCH 418/477] Multilang: Update pt-BR translations. (#35428) --- selfdrive/ui/translations/main_pt-BR.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index e25f345ae7..e977ee7b58 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -308,11 +308,11 @@ Disengage to Reset Calibration - + Desacione para Resetar a Calibração Resetting calibration will restart openpilot if the car is powered on. - + Resetar a calibração fará com que o openpilot reinicie se o carro estiver ligado. @@ -1130,11 +1130,11 @@ Isso pode levar até um minuto. Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. - + Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. Changing this setting will restart openpilot if the car is powered on. - + Alterar esta configuração fará com que o openpilot reinicie se o carro estiver ligado. From 9c28583171ba3336f3c93d6e9d567263dda79ac5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 3 Jun 2025 00:28:55 -0400 Subject: [PATCH 419/477] ui: include MADS enabled state to `engaged` check (#976) * ui: include MADS enabled state to `engaged` check * let's bump it up --- selfdrive/ui/sunnypilot/ui.h | 5 +++++ selfdrive/ui/ui.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/ui.h b/selfdrive/ui/sunnypilot/ui.h index 08e1d37b0f..a94dfca3b3 100644 --- a/selfdrive/ui/sunnypilot/ui.h +++ b/selfdrive/ui/sunnypilot/ui.h @@ -20,6 +20,11 @@ class UIStateSP : public UIState { public: UIStateSP(QObject *parent = 0); void updateStatus() override; + inline bool engaged() const override { + return scene.started && ( + (*sm)["selfdriveState"].getSelfdriveState().getEnabled() || (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads().getEnabled() + ); + } void setSunnylinkRoles(const std::vector &roles); void setSunnylinkDeviceUsers(const std::vector &users); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 6ef4f8e6d9..4ead8aecb9 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -72,7 +72,7 @@ class UIState : public QObject { public: UIState(QObject* parent = 0); virtual void updateStatus(); - inline bool engaged() const { + virtual inline bool engaged() const { return scene.started && (*sm)["selfdriveState"].getSelfdriveState().getEnabled(); } From 6f6adc10a83aa5ab775a2b0aaebb7ab4396f4268 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Mon, 2 Jun 2025 21:29:05 -0700 Subject: [PATCH 420/477] lagd: disable in release (#35426) * disable in release * pass * one liner * Update selfdrive/locationd/lagd.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- selfdrive/locationd/lagd.py | 11 ++++++++--- selfdrive/locationd/test/test_lagd.py | 12 ++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/selfdrive/locationd/lagd.py b/selfdrive/locationd/lagd.py index a61793e223..e070c16b1c 100755 --- a/selfdrive/locationd/lagd.py +++ b/selfdrive/locationd/lagd.py @@ -157,7 +157,8 @@ class LateralLagEstimator: block_count: int = BLOCK_NUM, min_valid_block_count: int = BLOCK_NUM_NEEDED, block_size: int = BLOCK_SIZE, window_sec: float = MOVING_WINDOW_SEC, okay_window_sec: float = MIN_OKAY_WINDOW_SEC, min_recovery_buffer_sec: float = MIN_RECOVERY_BUFFER_SEC, min_vego: float = MIN_VEGO, min_yr: float = MIN_ABS_YAW_RATE, min_ncc: float = MIN_NCC, - max_lat_accel: float = MAX_LAT_ACCEL, max_lat_accel_diff: float = MAX_LAT_ACCEL_DIFF, min_confidence: float = MIN_CONFIDENCE): + max_lat_accel: float = MAX_LAT_ACCEL, max_lat_accel_diff: float = MAX_LAT_ACCEL_DIFF, min_confidence: float = MIN_CONFIDENCE, + enabled: bool = True): self.dt = dt self.window_sec = window_sec self.okay_window_sec = okay_window_sec @@ -172,6 +173,7 @@ class LateralLagEstimator: self.min_confidence = min_confidence self.max_lat_accel = max_lat_accel self.max_lat_accel_diff = max_lat_accel_diff + self.enabled = enabled self.t = 0.0 self.lat_active = False @@ -206,7 +208,7 @@ class LateralLagEstimator: liveDelay = msg.liveDelay valid_mean_lag, valid_std, current_mean_lag, current_std = self.block_avg.get() - if self.block_avg.valid_blocks >= self.min_valid_block_count and not np.isnan(valid_mean_lag) and not np.isnan(valid_std): + if self.enabled and self.block_avg.valid_blocks >= self.min_valid_block_count and not np.isnan(valid_mean_lag) and not np.isnan(valid_std): if valid_std > MAX_LAG_STD: liveDelay.status = log.LiveDelayData.Status.invalid else: @@ -367,7 +369,10 @@ def main(): params = Params() CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) - lag_learner = LateralLagEstimator(CP, 1. / SERVICE_LIST['livePose'].frequency) + # TODO: remove me, lagd is in shadow mode on release + is_release = params.get_bool("IsReleaseBranch") + + lag_learner = LateralLagEstimator(CP, 1. / SERVICE_LIST['livePose'].frequency, enabled=not is_release) if (initial_lag_params := retrieve_initial_lag(params, CP)) is not None: lag, valid_blocks = initial_lag_params lag_learner.reset(lag, valid_blocks) diff --git a/selfdrive/locationd/test/test_lagd.py b/selfdrive/locationd/test/test_lagd.py index aad8cff229..a7f9c75ab4 100644 --- a/selfdrive/locationd/test/test_lagd.py +++ b/selfdrive/locationd/test/test_lagd.py @@ -108,6 +108,18 @@ class TestLagd: assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01) assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED + def test_disabled_estimator(self): + mocked_CP = car.CarParams(steerActuatorDelay=0.8) + estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0, enabled=False) + lag_frames = 5 + process_messages(estimator, lag_frames, int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_NUM_NEEDED * BLOCK_SIZE) + msg = estimator.get_msg(True) + assert msg.liveDelay.status == 'unestimated' + assert np.allclose(msg.liveDelay.lateralDelay, 1.0, atol=0.01) + assert np.allclose(msg.liveDelay.lateralDelayEstimate, lag_frames * DT, atol=0.01) + assert np.allclose(msg.liveDelay.lateralDelayEstimateStd, 0.0, atol=0.01) + assert msg.liveDelay.validBlocks == BLOCK_NUM_NEEDED + def test_estimator_masking(self): mocked_CP, lag_frames = car.CarParams(steerActuatorDelay=0.8), random.randint(1, 19) estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0, min_valid_block_count=1) From f78ba72a85c4509d4c14b80e96c00804ee7a0844 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 3 Jun 2025 12:32:13 +0800 Subject: [PATCH 421/477] ui: add timeout check for unresponsive system detection (#35433) add time out to check if messages have stopped arriving --- selfdrive/ui/onroad/alert_renderer.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index cc1b49a6a4..8efee54373 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from cereal import messaging, log from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEBUG_FPS from openpilot.system.ui.lib.label import gui_text_box from openpilot.selfdrive.ui.ui_state import ui_state @@ -70,17 +70,21 @@ class AlertRenderer: """Generate the current alert based on selfdrive state.""" ss = sm['selfdriveState'] - # Check if waiting to start - if sm.recv_frame['selfdriveState'] < ui_state.started_frame: - return ALERT_STARTUP_PENDING + # Check if selfdriveState messages have stopped arriving + if not sm.updated['selfdriveState']: + recv_frame = sm.recv_frame['selfdriveState'] + if (sm.frame - recv_frame) > 5 * DEBUG_FPS: + # Check if waiting to start + if recv_frame < ui_state.started_frame: + return ALERT_STARTUP_PENDING - # Handle selfdrive timeout - if TICI: - ss_missing = time.monotonic() - sm.recv_time['selfdriveState'] - if ss_missing > SELFDRIVE_STATE_TIMEOUT: - if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT: - return ALERT_CRITICAL_TIMEOUT - return ALERT_CRITICAL_REBOOT + # Handle selfdrive timeout + if TICI: + ss_missing = time.monotonic() - sm.recv_time['selfdriveState'] + if ss_missing > SELFDRIVE_STATE_TIMEOUT: + if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT: + return ALERT_CRITICAL_TIMEOUT + return ALERT_CRITICAL_REBOOT # No alert if size is none if ss.alertSize == 0: From 5505338ffbf080b8d2b060271a256856a38322ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Mon, 2 Jun 2025 21:54:37 -0700 Subject: [PATCH 422/477] model replay: less frames and less complexity (#35427) * Revert "ci: faster model_replay (#34036)" This reverts commit 847a5ce1f3ea18648e1483d2fb6e831f0cdbd40c. * fix conflict * trigger on test change * zst * give start and end frame * unused flags * no print * whitespace * fix plotting * slice correct * no print * Just start from beginning --- selfdrive/test/process_replay/model_replay.py | 68 ++++++------------- tools/lib/framereader.py | 22 ------ 2 files changed, 20 insertions(+), 70 deletions(-) diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py index 0578a61588..201baf6b0e 100755 --- a/selfdrive/test/process_replay/model_replay.py +++ b/selfdrive/test/process_replay/model_replay.py @@ -15,15 +15,15 @@ from openpilot.system.hardware import PC from openpilot.tools.lib.openpilotci import get_url from openpilot.selfdrive.test.process_replay.compare_logs import compare_logs, format_diff from openpilot.selfdrive.test.process_replay.process_replay import get_process_config, replay_process -from openpilot.tools.lib.framereader import FrameReader, NumpyFrameReader +from openpilot.tools.lib.framereader import FrameReader from openpilot.tools.lib.logreader import LogReader, save_log from openpilot.tools.lib.github_utils import GithubUtils TEST_ROUTE = "8494c69d3c710e81|000001d4--2648a9a404" SEGMENT = 4 -MAX_FRAMES = 100 if PC else 400 +START_FRAME = 0 +END_FRAME = 60 -NO_MODEL = "NO_MODEL" in os.environ SEND_EXTRA_INPUTS = bool(int(os.getenv("SEND_EXTRA_INPUTS", "0"))) DATA_TOKEN = os.getenv("CI_ARTIFACTS_TOKEN","") @@ -125,16 +125,15 @@ def comment_replay_report(proposed, master, full_logs): comment = f"ref for commit {commit}: {link}/{log_name}" + diff_plots + all_plots GITHUB.comment_on_pr(comment, PR_BRANCH, "commaci-public", True) -def trim_logs_to_max_frames(logs, max_frames, frs_types, include_all_types): +def trim_logs(logs, start_frame, end_frame, frs_types, include_all_types): all_msgs = [] cam_state_counts = defaultdict(int) - # keep adding messages until cam states are equal to MAX_FRAMES for msg in sorted(logs, key=lambda m: m.logMonoTime): - all_msgs.append(msg) if msg.which() in frs_types: cam_state_counts[msg.which()] += 1 - - if all(cam_state_counts[state] == max_frames for state in frs_types): + if any(cam_state_counts[state] >= start_frame for state in frs_types): + all_msgs.append(msg) + if all(cam_state_counts[state] == end_frame for state in frs_types): break if len(include_all_types) != 0: @@ -146,9 +145,9 @@ def trim_logs_to_max_frames(logs, max_frames, frs_types, include_all_types): def model_replay(lr, frs): # modeld is using frame pairs - modeld_logs = trim_logs_to_max_frames(lr, MAX_FRAMES, {"roadCameraState", "wideRoadCameraState"}, - {"roadEncodeIdx", "wideRoadEncodeIdx", "carParams", "carState", "carControl"}) - dmodeld_logs = trim_logs_to_max_frames(lr, MAX_FRAMES, {"driverCameraState"}, {"driverEncodeIdx", "carParams"}) + modeld_logs = trim_logs(lr, START_FRAME, END_FRAME, {"roadCameraState", "wideRoadCameraState"}, + {"roadEncodeIdx", "wideRoadEncodeIdx", "carParams", "carState", "carControl", "can"}) + dmodeld_logs = trim_logs(lr, START_FRAME, END_FRAME, {"driverCameraState"}, {"driverEncodeIdx", "carParams", "can"}) if not SEND_EXTRA_INPUTS: modeld_logs = [msg for msg in modeld_logs if msg.which() != 'liveCalibration'] @@ -165,9 +164,6 @@ def model_replay(lr, frs): dmonitoringmodeld = get_process_config("dmonitoringmodeld") modeld_msgs = replay_process(modeld, modeld_logs, frs) - if isinstance(frs['roadCameraState'], NumpyFrameReader): - del frs['roadCameraState'].frames - del frs['wideRoadCameraState'].frames dmonitoringmodeld_msgs = replay_process(dmonitoringmodeld, dmodeld_logs, frs) msgs = modeld_msgs + dmonitoringmodeld_msgs @@ -198,42 +194,21 @@ def model_replay(lr, frs): return msgs -def get_frames(): - regen_cache = "--regen-cache" in sys.argv - cache = "--cache" in sys.argv or not PC or regen_cache - videos = ('fcamera.hevc', 'dcamera.hevc', 'ecamera.hevc') - cams = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') - - if cache: - frames_cache = '/tmp/model_replay_cache' if PC else '/data/model_replay_cache' - os.makedirs(frames_cache, exist_ok=True) - - cache_size = 200 - for v in videos: - if not all(os.path.isfile(f'{frames_cache}/{TEST_ROUTE}_{v}_{i}.npy') for i in range(MAX_FRAMES//cache_size)) or regen_cache: - f = FrameReader(get_url(TEST_ROUTE, SEGMENT, v)).get(0, MAX_FRAMES + 1, pix_fmt="nv12") - print(f'Caching {v}...') - for i in range(MAX_FRAMES//cache_size): - np.save(f'{frames_cache}/{TEST_ROUTE}_{v}_{i}', f[(i * cache_size) + 1:((i + 1) * cache_size) + 1]) - del f - - return {c : NumpyFrameReader(f"{frames_cache}/{TEST_ROUTE}_{v}", 1928, 1208, cache_size) for c,v in zip(cams, videos, strict=True)} - else: - return {c : FrameReader(get_url(TEST_ROUTE, SEGMENT, v), readahead=True) for c,v in zip(cams, videos, strict=True)} - - if __name__ == "__main__": update = "--update" in sys.argv or (os.getenv("GIT_BRANCH", "") == 'master') replay_dir = os.path.dirname(os.path.abspath(__file__)) # load logs lr = list(LogReader(get_url(TEST_ROUTE, SEGMENT, "rlog.zst"))) - frs = get_frames() + frs = { + 'roadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "fcamera.hevc"), readahead=True), + 'driverCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "dcamera.hevc"), readahead=True), + 'wideRoadCameraState': FrameReader(get_url(TEST_ROUTE, SEGMENT, "ecamera.hevc"), readahead=True) + } log_msgs = [] # run replays - if not NO_MODEL: - log_msgs += model_replay(lr, frs) + log_msgs += model_replay(lr, frs) # get diff failed = False @@ -242,13 +217,10 @@ if __name__ == "__main__": try: all_logs = list(LogReader(GITHUB.get_file_url(MODEL_REPLAY_BUCKET, log_fn))) cmp_log = [] - - # logs are ordered based on type: modelV2, drivingModelData, driverStateV2 - if not NO_MODEL: - model_start_index = next(i for i, m in enumerate(all_logs) if m.which() in ("modelV2", "drivingModelData", "cameraOdometry")) - cmp_log += all_logs[model_start_index:model_start_index + MAX_FRAMES*3] - dmon_start_index = next(i for i, m in enumerate(all_logs) if m.which() == "driverStateV2") - cmp_log += all_logs[dmon_start_index:dmon_start_index + MAX_FRAMES] + model_start_index = next(i for i, m in enumerate(all_logs) if m.which() in ("modelV2", "drivingModelData", "cameraOdometry")) + cmp_log += all_logs[model_start_index+START_FRAME*3:model_start_index + END_FRAME*3] + dmon_start_index = next(i for i, m in enumerate(all_logs) if m.which() == "driverStateV2") + cmp_log += all_logs[dmon_start_index+START_FRAME:dmon_start_index + END_FRAME] ignore = [ 'logMonoTime', diff --git a/tools/lib/framereader.py b/tools/lib/framereader.py index 7c01992a28..275b9b65b8 100644 --- a/tools/lib/framereader.py +++ b/tools/lib/framereader.py @@ -535,25 +535,3 @@ def FrameIterator(fn, pix_fmt, **kwargs): else: for i in range(fr.frame_count): yield fr.get(i, pix_fmt=pix_fmt)[0] - - -class NumpyFrameReader: - def __init__(self, name, w, h, cache_size): - self.name = name - self.pos = -1 - self.frames = None - self.w = w - self.h = h - self.cache_size = cache_size - - def close(self): - pass - - def get(self, num, count=1, pix_fmt="nv12"): - num -= 1 - q = num // self.cache_size - if q != self.pos: - del self.frames - self.pos = q - self.frames = np.load(f'{self.name}_{self.pos}.npy') - return [self.frames[num % self.cache_size]] From c7767bddfe3b1f29c601d07ebbbcee2824587dd6 Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Tue, 3 Jun 2025 04:12:08 -0700 Subject: [PATCH 423/477] UI: prep for custom model rendering (#920) * Refactor and extend ModelRenderer for custom Sunnypilot logic Refactored `ModelRenderer` to `ModelRendererSP` with enhanced features such as lane line updates, path drawing, and lead management for Sunnypilot. Introduced new methods for model updates, lead drawing, and improved path rendering with experimental mode support. Ensured compatibility by integrating with Sunnypilot-specific HUD and camera components. * Update selfdrive/ui/sunnypilot/qt/onroad/model.cc * Refactor `ModelRenderer` for modularity Moved constants and `get_path_length_idx` function to the header file for reuse and clarity. Updated `drawPath` and related methods to better handle surface dimensions, improving rendering flexibility. Made key functions virtual to allow further customization in derived classes. * Cleaning logic on ModelRenderSP Given that we've refactored slightly the original ModelRender, we no longer need to duplicate the logic on our own implementation --------- Co-authored-by: DevTekVE Co-authored-by: Jason Wen --- selfdrive/ui/qt/onroad/annotated_camera.h | 2 ++ selfdrive/ui/qt/onroad/model.cc | 15 +-------------- selfdrive/ui/qt/onroad/model.h | 22 ++++++++++++++++++++-- selfdrive/ui/sunnypilot/qt/onroad/model.h | 2 +- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index 219b39546f..e3ca837907 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -9,7 +9,9 @@ #ifdef SUNNYPILOT #include "selfdrive/ui/sunnypilot/qt/onroad/buttons.h" #include "selfdrive/ui/sunnypilot/qt/onroad/hud.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/model.h" #define ExperimentalButton ExperimentalButtonSP +#define ModelRenderer ModelRendererSP #else #include "selfdrive/ui/qt/onroad/buttons.h" #include "selfdrive/ui/qt/onroad/hud.h" diff --git a/selfdrive/ui/qt/onroad/model.cc b/selfdrive/ui/qt/onroad/model.cc index 52902abdc8..dc801e591f 100644 --- a/selfdrive/ui/qt/onroad/model.cc +++ b/selfdrive/ui/qt/onroad/model.cc @@ -1,18 +1,5 @@ #include "selfdrive/ui/qt/onroad/model.h" -constexpr int CLIP_MARGIN = 500; -constexpr float MIN_DRAW_DISTANCE = 10.0; -constexpr float MAX_DRAW_DISTANCE = 100.0; - -static int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_height) { - const auto &line_x = line.getX(); - int max_idx = 0; - for (int i = 1; i < line_x.size() && line_x[i] <= path_height; ++i) { - max_idx = i; - } - return max_idx; -} - void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) { auto *s = uiState(); auto &sm = *(s->sm); @@ -35,7 +22,7 @@ void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) { update_model(model, lead_one); drawLaneLines(painter); - drawPath(painter, model, surface_rect.height()); + drawPath(painter, model, surface_rect); if (longitudinal_control && sm.alive("radarState")) { update_leads(radar_state, model.getPosition()); diff --git a/selfdrive/ui/qt/onroad/model.h b/selfdrive/ui/qt/onroad/model.h index 7e1b43acb4..85eb236e76 100644 --- a/selfdrive/ui/qt/onroad/model.h +++ b/selfdrive/ui/qt/onroad/model.h @@ -9,21 +9,39 @@ #include "selfdrive/ui/ui.h" #endif +constexpr int CLIP_MARGIN = 500; +constexpr float MIN_DRAW_DISTANCE = 10.0; +constexpr float MAX_DRAW_DISTANCE = 100.0; + +inline int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_height) { + const auto &line_x = line.getX(); + int max_idx = 0; + for (int i = 1; i < line_x.size() && line_x[i] <= path_height; ++i) { + max_idx = i; + } + return max_idx; +} + class ModelRenderer { public: + virtual ~ModelRenderer() = default; + ModelRenderer() {} void setTransform(const Eigen::Matrix3f &transform) { car_space_transform = transform; } void draw(QPainter &painter, const QRect &surface_rect); -private: +protected: bool mapToScreen(float in_x, float in_y, float in_z, QPointF *out); void mapLineToPolygon(const cereal::XYZTData::Reader &line, float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert = true); void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, const QRect &surface_rect); void update_leads(const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line); - void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead); + virtual void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead); void drawLaneLines(QPainter &painter); void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, int height); + virtual void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &surface_rect) {; + drawPath(painter, model, surface_rect.height()); + } void updatePathGradient(QLinearGradient &bg); QColor blendColors(const QColor &start, const QColor &end, float t); diff --git a/selfdrive/ui/sunnypilot/qt/onroad/model.h b/selfdrive/ui/sunnypilot/qt/onroad/model.h index e8594629cb..8569e58f66 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/model.h +++ b/selfdrive/ui/sunnypilot/qt/onroad/model.h @@ -11,5 +11,5 @@ class ModelRendererSP : public ModelRenderer { public: - ModelRendererSP() {} + ModelRendererSP() = default; }; From 10dc40db04d29260ffcc6684d5271aab2c659687 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 02:07:42 +0800 Subject: [PATCH 424/477] ui: fill the bg with disengaged color if not started (#35434) fill the bg with disengaged color if not started --- selfdrive/ui/onroad/augmented_road_view.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index f987d70c9c..58ba822e35 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -45,7 +45,8 @@ class AugmentedRoadView(CameraView): def render(self, rect): # Only render when system is started to avoid invalid data access - if not ui_state.started: + if not ui_state.started or not self.frame: + rl.draw_rectangle_rec(rect, BORDER_COLORS[UIStatus.DISENGAGED]) return # Update calibration before rendering From e6f55a7eea008f042a76e82fa340e6ffbff4e510 Mon Sep 17 00:00:00 2001 From: Nayan Date: Tue, 3 Jun 2025 16:05:02 -0400 Subject: [PATCH 425/477] ui: visuals panel (#919) --- selfdrive/ui/sunnypilot/SConscript | 1 + .../sunnypilot/qt/offroad/settings/settings.cc | 2 ++ .../qt/offroad/settings/visuals_panel.cc | 12 ++++++++++++ .../qt/offroad/settings/visuals_panel.h | 18 ++++++++++++++++++ selfdrive/ui/tests/test_ui/run.py | 7 +++++++ .../selfdrive/assets/offroad/icon_visuals.png | 3 +++ 6 files changed, 43 insertions(+) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h create mode 100644 sunnypilot/selfdrive/assets/offroad/icon_visuals.png diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index b5d18c5b91..0353c9babd 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -31,6 +31,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/sunnylink/sponsor_widget.cc", "sunnypilot/qt/offroad/settings/trips_panel.cc", "sunnypilot/qt/offroad/settings/vehicle_panel.cc", + "sunnypilot/qt/offroad/settings/visuals_panel.cc", "sunnypilot/qt/onroad/annotated_camera.cc", "sunnypilot/qt/onroad/buttons.cc", "sunnypilot/qt/onroad/hud.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc index ad057ac714..e91de4c903 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc @@ -20,6 +20,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h" TogglesPanelSP::TogglesPanelSP(SettingsWindowSP *parent) : TogglesPanel(parent) { QObject::connect(uiStateSP(), &UIStateSP::uiUpdate, this, &TogglesPanelSP::updateState); @@ -83,6 +84,7 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { PanelInfo(" " + tr("Models"), new ModelsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_models.png"), PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"), PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/icons/speed_limit.png"), + PanelInfo(" " + tr("Visuals"), new VisualsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_visuals.png"), PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"), PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"), PanelInfo(" " + tr("Firehose"), new FirehosePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_firehose.svg"), diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc new file mode 100644 index 0000000000..cf9729be18 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h" + +VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) { + +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h new file mode 100644 index 0000000000..9f58104c4e --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +class VisualsPanel : public QWidget { + Q_OBJECT + +public: + explicit VisualsPanel(QWidget *parent = nullptr); + +}; diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 0b9d266ad6..536917a1a6 100755 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -252,6 +252,12 @@ def setup_settings_driving(click, pm: PubMaster, scroll=None): click(278, 962) time.sleep(UI_DELAY) +def setup_settings_visuals(click, pm: PubMaster, scroll=None): + setup_settings_device(click, pm) + scroll(-400, 278, 962) + click(278, 560) + time.sleep(UI_DELAY) + def setup_settings_trips(click, pm: PubMaster, scroll=None): setup_settings_device(click, pm) scroll(-400, 278, 962) @@ -307,6 +313,7 @@ CASES.update({ "settings_steering_mads": setup_settings_steering_mads, "settings_steering_alc": setup_settings_steering_alc, "settings_driving": setup_settings_driving, + "settings_visuals": setup_settings_visuals, "settings_trips": setup_settings_trips, "settings_vehicle": setup_settings_vehicle, }) diff --git a/sunnypilot/selfdrive/assets/offroad/icon_visuals.png b/sunnypilot/selfdrive/assets/offroad/icon_visuals.png new file mode 100644 index 0000000000..3cc9c3b145 --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_visuals.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ecaac6fb687fcab3300d1fe2dfea96fb49734d634a663e2c02ee0b26ebd773e +size 20632 From da58feb869fef108f2d4c5e9d1b90dcfe661ff96 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 04:08:10 +0800 Subject: [PATCH 426/477] system/ui: fix wifi network icon resize artifacts (#35440) fix wifi network icon resize artifacts --- system/ui/widgets/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/widgets/network.py b/system/ui/widgets/network.py index 46274dbf7e..80f55ebf56 100644 --- a/system/ui/widgets/network.py +++ b/system/ui/widgets/network.py @@ -15,7 +15,7 @@ NM_DEVICE_STATE_NEED_AUTH = 60 MIN_PASSWORD_LENGTH = 8 MAX_PASSWORD_LENGTH = 64 ITEM_HEIGHT = 160 -ICON_SIZE = 49 +ICON_SIZE = 50 STRENGTH_ICONS = [ "icons/wifi_strength_low.png", From 928dc6259b6e1cec383cd02038ff27f7dd2f9ea5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 3 Jun 2025 14:51:15 -0700 Subject: [PATCH 427/477] Revert "ui: fill the bg with disengaged color if not started" (#35441) Revert "ui: fill the bg with disengaged color if not started (#35434)" This reverts commit 10dc40db04d29260ffcc6684d5271aab2c659687. --- selfdrive/ui/onroad/augmented_road_view.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 58ba822e35..f987d70c9c 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -45,8 +45,7 @@ class AugmentedRoadView(CameraView): def render(self, rect): # Only render when system is started to avoid invalid data access - if not ui_state.started or not self.frame: - rl.draw_rectangle_rec(rect, BORDER_COLORS[UIStatus.DISENGAGED]) + if not ui_state.started: return # Update calibration before rendering From ae8076e729b5e293b11b07055c80acdcc4282f64 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 05:51:56 +0800 Subject: [PATCH 428/477] selfdrive/ui: add experimental mode toggle button with visual indicator (#35439) add experimental mode toggle button with visual indicator --- selfdrive/ui/onroad/hud_renderer.py | 55 ++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index ed0b7cc974..c2482771de 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -4,6 +4,7 @@ from cereal.messaging import SubMaster from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.common.conversions import Conversions as CV +from openpilot.common.params import Params # Constants SET_SPEED_NA = 255 @@ -61,8 +62,17 @@ class HudRenderer: self.set_speed: float = SET_SPEED_NA self.speed: float = 0.0 self.v_ego_cluster_seen: bool = False + self._experimental_mode: bool = False + self._engageable: bool = False + self.font_metrics_cache: dict[[str, int, str], rl.Vector2] = {} + + self._white_color: rl.Color = rl.Color(255, 255, 255, 255) self._wheel_texture: rl.Texture = gui_app.texture('icons/chffr_wheel.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size) + self._experimental_texture: rl.Texture = gui_app.texture('icons/experimental.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size) + self._wheel_rect: rl.Rectangle = rl.Rectangle(0, 0, UI_CONFIG.button_size, UI_CONFIG.button_size) + self._params = Params() + self._font_semi_bold: rl.Font = gui_app.font(FontWeight.SEMI_BOLD) self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM) @@ -94,9 +104,15 @@ class HudRenderer: speed_conversion = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH self.speed = max(0.0, v_ego * speed_conversion) + selfdrive_state = sm["selfdriveState"] + self._experimental_mode = selfdrive_state.experimentalMode + self._engageable = selfdrive_state.engageable or selfdrive_state.enabled + def draw(self, rect: rl.Rectangle, sm: SubMaster) -> None: """Render HUD elements to the screen.""" self._update_state(sm) + + # Draw the header background rl.draw_rectangle_gradient_v( int(rect.x), int(rect.y), @@ -110,7 +126,17 @@ class HudRenderer: self._draw_set_speed(rect) self._draw_current_speed(rect) - self._draw_wheel_icon(rect) + + self._wheel_rect.x = rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size + self._wheel_rect.y = rect.y + UI_CONFIG.border_size + self._handle_click() + self._draw_wheel_icon() + + def _handle_click(self) -> None: + if (rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and + rl.check_collision_point_rec(rl.get_mouse_position(), self._wheel_rect)): + if self._experimental_toggle_allowed(): + self._params.putBool("ExperimentalMode", not self._experimental_mode) def _draw_set_speed(self, rect: rl.Rectangle) -> None: """Draw the MAX speed indicator box.""" @@ -167,15 +193,18 @@ class HudRenderer: unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent) - def _draw_wheel_icon(self, rect: rl.Rectangle) -> None: + def _draw_wheel_icon(self) -> None: """Draw the steering wheel icon with status-based opacity.""" - center_x = int(rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size / 2) - center_y = int(rect.y + UI_CONFIG.border_size + UI_CONFIG.button_size / 2) - rl.draw_circle(center_x, center_y, UI_CONFIG.button_size / 2, COLORS.black_translucent) + center_x = int(self._wheel_rect.x + self._wheel_rect.width // 2) + center_y = int(self._wheel_rect.y + self._wheel_rect.height // 2) - opacity = 0.7 if ui_state.status == UIStatus.DISENGAGED else 1.0 - img_pos = rl.Vector2(center_x - self._wheel_texture.width / 2, center_y - self._wheel_texture.height / 2) - rl.draw_texture_v(self._wheel_texture, img_pos, rl.Color(255, 255, 255, int(255 * opacity))) + mouse_down = (rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and + rl.check_collision_point_rec(rl.get_mouse_position(), self._wheel_rect)) + self._white_color.a = 180 if (mouse_down or not self._engageable) else 255 + texture = self._experimental_texture if self._experimental_mode else self._wheel_texture + + rl.draw_circle(center_x, center_y, UI_CONFIG.button_size / 2, COLORS.black_translucent) + rl.draw_texture(texture, center_x - texture.width // 2, center_y - texture.height // 2, self._white_color) def _measure_text(self, text: str, font: rl.Font, font_size: int, font_type: str) -> rl.Vector2: """Measure text dimensions with caching.""" @@ -183,3 +212,13 @@ class HudRenderer: if key not in self.font_metrics_cache: self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) return self.font_metrics_cache[key] + + def _experimental_toggle_allowed(self): + if not self._params.get_bool("ExperimentalModeConfirmed"): + return False + + car_params = ui_state.sm["carParams"] + if car_params.alphaLongitudinalAvailable: + return self._params.get_bool("AlphaLongitudinalEnabled") + else: + return car_params.openpilotLongitudinalControl From 68b043606c20e43acd66960db19784562726aa65 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 05:52:43 +0800 Subject: [PATCH 429/477] ui: fix alert timeout detection using wrong FPS constant (#35438) fix alert timeiout detection using wrong FPS constant --- selfdrive/ui/onroad/alert_renderer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 8efee54373..37276b11a4 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from cereal import messaging, log from openpilot.system.hardware import TICI -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEBUG_FPS +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS from openpilot.system.ui.lib.label import gui_text_box from openpilot.selfdrive.ui.ui_state import ui_state @@ -73,7 +73,7 @@ class AlertRenderer: # Check if selfdriveState messages have stopped arriving if not sm.updated['selfdriveState']: recv_frame = sm.recv_frame['selfdriveState'] - if (sm.frame - recv_frame) > 5 * DEBUG_FPS: + if (sm.frame - recv_frame) > 5 * DEFAULT_FPS: # Check if waiting to start if recv_frame < ui_state.started_frame: return ALERT_STARTUP_PENDING From fb6243688dd508f9e467ad7a83b52e73d4219758 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 3 Jun 2025 14:53:26 -0700 Subject: [PATCH 430/477] raylib: rename DEBUG_FPS --- system/ui/README.md | 2 +- system/ui/lib/application.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/ui/README.md b/system/ui/README.md index c71b77ab66..85d32bfd6c 100644 --- a/system/ui/README.md +++ b/system/ui/README.md @@ -3,7 +3,7 @@ The user interfaces here are built with [raylib](https://www.raylib.com/). Quick start: -* set `DEBUG_FPS=1` to show the FPS +* set `SHOW_FPS=1` to show the FPS * set `STRICT_MODE=1` to kill the app if it drops too much below 60fps * set `SCALE=1.5` to scale the entire UI by 1.5x * https://www.raylib.com/cheatsheet/cheatsheet.html diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index fcef55d531..c2d6ac8e2c 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -13,7 +13,7 @@ FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" -DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' +SHOW_FPS = os.getenv("SHOW_FPS") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1' SCALE = float(os.getenv("SCALE", "1.0")) @@ -158,7 +158,7 @@ class GuiApplication: dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) - if DEBUG_FPS: + if SHOW_FPS: rl.draw_fps(10, 10) rl.end_drawing() From 2dcab07be7d087cd381c0256817025aac3004fc6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 3 Jun 2025 14:59:07 -0700 Subject: [PATCH 431/477] Revert "selfdrive/ui: add experimental mode toggle button with visual indicator" (#35442) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "selfdrive/ui: add experimental mode toggle button with visual indicat…" This reverts commit ae8076e729b5e293b11b07055c80acdcc4282f64. --- selfdrive/ui/onroad/hud_renderer.py | 55 +++++------------------------ 1 file changed, 8 insertions(+), 47 deletions(-) diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index c2482771de..ed0b7cc974 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -4,7 +4,6 @@ from cereal.messaging import SubMaster from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.common.conversions import Conversions as CV -from openpilot.common.params import Params # Constants SET_SPEED_NA = 255 @@ -62,17 +61,8 @@ class HudRenderer: self.set_speed: float = SET_SPEED_NA self.speed: float = 0.0 self.v_ego_cluster_seen: bool = False - self._experimental_mode: bool = False - self._engageable: bool = False - self.font_metrics_cache: dict[[str, int, str], rl.Vector2] = {} - - self._white_color: rl.Color = rl.Color(255, 255, 255, 255) self._wheel_texture: rl.Texture = gui_app.texture('icons/chffr_wheel.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size) - self._experimental_texture: rl.Texture = gui_app.texture('icons/experimental.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size) - self._wheel_rect: rl.Rectangle = rl.Rectangle(0, 0, UI_CONFIG.button_size, UI_CONFIG.button_size) - self._params = Params() - self._font_semi_bold: rl.Font = gui_app.font(FontWeight.SEMI_BOLD) self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self._font_medium: rl.Font = gui_app.font(FontWeight.MEDIUM) @@ -104,15 +94,9 @@ class HudRenderer: speed_conversion = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH self.speed = max(0.0, v_ego * speed_conversion) - selfdrive_state = sm["selfdriveState"] - self._experimental_mode = selfdrive_state.experimentalMode - self._engageable = selfdrive_state.engageable or selfdrive_state.enabled - def draw(self, rect: rl.Rectangle, sm: SubMaster) -> None: """Render HUD elements to the screen.""" self._update_state(sm) - - # Draw the header background rl.draw_rectangle_gradient_v( int(rect.x), int(rect.y), @@ -126,17 +110,7 @@ class HudRenderer: self._draw_set_speed(rect) self._draw_current_speed(rect) - - self._wheel_rect.x = rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size - self._wheel_rect.y = rect.y + UI_CONFIG.border_size - self._handle_click() - self._draw_wheel_icon() - - def _handle_click(self) -> None: - if (rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and - rl.check_collision_point_rec(rl.get_mouse_position(), self._wheel_rect)): - if self._experimental_toggle_allowed(): - self._params.putBool("ExperimentalMode", not self._experimental_mode) + self._draw_wheel_icon(rect) def _draw_set_speed(self, rect: rl.Rectangle) -> None: """Draw the MAX speed indicator box.""" @@ -193,18 +167,15 @@ class HudRenderer: unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent) - def _draw_wheel_icon(self) -> None: + def _draw_wheel_icon(self, rect: rl.Rectangle) -> None: """Draw the steering wheel icon with status-based opacity.""" - center_x = int(self._wheel_rect.x + self._wheel_rect.width // 2) - center_y = int(self._wheel_rect.y + self._wheel_rect.height // 2) - - mouse_down = (rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and - rl.check_collision_point_rec(rl.get_mouse_position(), self._wheel_rect)) - self._white_color.a = 180 if (mouse_down or not self._engageable) else 255 - texture = self._experimental_texture if self._experimental_mode else self._wheel_texture - + center_x = int(rect.x + rect.width - UI_CONFIG.border_size - UI_CONFIG.button_size / 2) + center_y = int(rect.y + UI_CONFIG.border_size + UI_CONFIG.button_size / 2) rl.draw_circle(center_x, center_y, UI_CONFIG.button_size / 2, COLORS.black_translucent) - rl.draw_texture(texture, center_x - texture.width // 2, center_y - texture.height // 2, self._white_color) + + opacity = 0.7 if ui_state.status == UIStatus.DISENGAGED else 1.0 + img_pos = rl.Vector2(center_x - self._wheel_texture.width / 2, center_y - self._wheel_texture.height / 2) + rl.draw_texture_v(self._wheel_texture, img_pos, rl.Color(255, 255, 255, int(255 * opacity))) def _measure_text(self, text: str, font: rl.Font, font_size: int, font_type: str) -> rl.Vector2: """Measure text dimensions with caching.""" @@ -212,13 +183,3 @@ class HudRenderer: if key not in self.font_metrics_cache: self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) return self.font_metrics_cache[key] - - def _experimental_toggle_allowed(self): - if not self._params.get_bool("ExperimentalModeConfirmed"): - return False - - car_params = ui_state.sm["carParams"] - if car_params.alphaLongitudinalAvailable: - return self._params.get_bool("AlphaLongitudinalEnabled") - else: - return car_params.openpilotLongitudinalControl From 4c8f15221e97a17f2d83454363a099b7f379b2d3 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 3 Jun 2025 22:07:52 -0700 Subject: [PATCH 432/477] Tesla: move safety out of debug (#35447) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index f01c4b82b1..0d7fef4422 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit f01c4b82b1cd41ef8053a038ad631bbd31105fc6 +Subproject commit 0d7fef4422456aeb456fa895da1617a1f61f6896 From fc0bb721472a8f3ad91e824bf8279f2e763c2884 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 14:06:46 +0800 Subject: [PATCH 433/477] selfdrive/ui: enable conflate mode in VisionIpcClient to prevent stale frame rendering (#35445) use conflate mode in VisionIpcClient to prevent stale frame rendering --- selfdrive/ui/onroad/cameraview.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 329b8349ba..d59e7b0a48 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -57,7 +57,7 @@ else: class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): - self.client = VisionIpcClient(name, stream_type, False) + self.client = VisionIpcClient(name, stream_type, conflate=True) self._name = name self._stream_type = stream_type @@ -90,7 +90,7 @@ class CameraView: self._clear_textures() self.frame = None self._stream_type = stream_type - self.client = VisionIpcClient(self._name, stream_type, False) + self.client = VisionIpcClient(self._name, stream_type, conflate=True) @property def stream_type(self) -> VisionStreamType: From 7ee50e7b870a753e65714b5127fabf4ecbdce3a7 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 14:07:29 +0800 Subject: [PATCH 434/477] system/ui: add text measurement caching (#35436) --- selfdrive/ui/onroad/alert_renderer.py | 10 ++-------- selfdrive/ui/onroad/hud_renderer.py | 17 +++++------------ system/ui/lib/text_measure.py | 13 +++++++++++++ 3 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 system/ui/lib/text_measure.py diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 37276b11a4..7b0128271e 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -5,6 +5,7 @@ from cereal import messaging, log from openpilot.system.hardware import TICI from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS from openpilot.system.ui.lib.label import gui_text_box +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.ui_state import ui_state @@ -64,7 +65,6 @@ class AlertRenderer: def __init__(self): self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) - self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {} def get_alert(self, sm: messaging.SubMaster) -> Alert | None: """Generate the current alert based on selfdrive state.""" @@ -152,14 +152,8 @@ class AlertRenderer: text_rect.y = rect.y + rect.height // 2 gui_text_box(text_rect, alert.text2, ALERT_FONT_BIG, alignment=align_ment) - def _measure_text(self, font: rl.Font, text: str, font_size: int, font_type: str) -> rl.Vector2: - key = (text, font_size, font_type) - if key not in self.font_metrics_cache: - self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) - return self.font_metrics_cache[key] - def _draw_centered(self, text, rect, font, font_size, center_y=True, color=rl.WHITE) -> None: - text_size = self._measure_text(font, text, font_size, 'bold' if font == self.font_bold else 'regular') + text_size = measure_text_cached(font, text, font_size) x = rect.x + (rect.width - text_size.x) / 2 y = rect.y + ((rect.height - text_size.y) / 2 if center_y else 0) rl.draw_text_ex(font, text, rl.Vector2(x, y), font_size, 0, color) diff --git a/selfdrive/ui/onroad/hud_renderer.py b/selfdrive/ui/onroad/hud_renderer.py index ed0b7cc974..0e893acfe9 100644 --- a/selfdrive/ui/onroad/hud_renderer.py +++ b/selfdrive/ui/onroad/hud_renderer.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from cereal.messaging import SubMaster from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.common.conversions import Conversions as CV # Constants @@ -61,7 +62,6 @@ class HudRenderer: self.set_speed: float = SET_SPEED_NA self.speed: float = 0.0 self.v_ego_cluster_seen: bool = False - self.font_metrics_cache: dict[[str, int, str], rl.Vector2] = {} self._wheel_texture: rl.Texture = gui_app.texture('icons/chffr_wheel.png', UI_CONFIG.wheel_icon_size, UI_CONFIG.wheel_icon_size) self._font_semi_bold: rl.Font = gui_app.font(FontWeight.SEMI_BOLD) self._font_bold: rl.Font = gui_app.font(FontWeight.BOLD) @@ -134,7 +134,7 @@ class HudRenderer: max_color = COLORS.override max_text = "MAX" - max_text_width = self._measure_text(max_text, self._font_semi_bold, FONT_SIZES.max_speed, 'semi_bold').x + max_text_width = measure_text_cached(self._font_semi_bold, max_text, FONT_SIZES.max_speed).x rl.draw_text_ex( self._font_semi_bold, max_text, @@ -145,7 +145,7 @@ class HudRenderer: ) set_speed_text = CRUISE_DISABLED_CHAR if not self.is_cruise_set else str(round(self.set_speed)) - speed_text_width = self._measure_text(set_speed_text, self._font_bold, FONT_SIZES.set_speed, 'bold').x + speed_text_width = measure_text_cached(self._font_bold, set_speed_text, FONT_SIZES.set_speed).x rl.draw_text_ex( self._font_bold, set_speed_text, @@ -158,12 +158,12 @@ class HudRenderer: def _draw_current_speed(self, rect: rl.Rectangle) -> None: """Draw the current vehicle speed and unit.""" speed_text = str(round(self.speed)) - speed_text_size = self._measure_text(speed_text, self._font_bold, FONT_SIZES.current_speed, 'bold') + speed_text_size = measure_text_cached(self._font_bold, speed_text, FONT_SIZES.current_speed) speed_pos = rl.Vector2(rect.x + rect.width / 2 - speed_text_size.x / 2, 180 - speed_text_size.y / 2) rl.draw_text_ex(self._font_bold, speed_text, speed_pos, FONT_SIZES.current_speed, 0, COLORS.white) unit_text = "km/h" if ui_state.is_metric else "mph" - unit_text_size = self._measure_text(unit_text, self._font_medium, FONT_SIZES.speed_unit, 'medium') + unit_text_size = measure_text_cached(self._font_medium, unit_text, FONT_SIZES.speed_unit) unit_pos = rl.Vector2(rect.x + rect.width / 2 - unit_text_size.x / 2, 290 - unit_text_size.y / 2) rl.draw_text_ex(self._font_medium, unit_text, unit_pos, FONT_SIZES.speed_unit, 0, COLORS.white_translucent) @@ -176,10 +176,3 @@ class HudRenderer: opacity = 0.7 if ui_state.status == UIStatus.DISENGAGED else 1.0 img_pos = rl.Vector2(center_x - self._wheel_texture.width / 2, center_y - self._wheel_texture.height / 2) rl.draw_texture_v(self._wheel_texture, img_pos, rl.Color(255, 255, 255, int(255 * opacity))) - - def _measure_text(self, text: str, font: rl.Font, font_size: int, font_type: str) -> rl.Vector2: - """Measure text dimensions with caching.""" - key = (text, font_size, font_type) - if key not in self.font_metrics_cache: - self.font_metrics_cache[key] = rl.measure_text_ex(font, text, font_size, 0) - return self.font_metrics_cache[key] diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py new file mode 100644 index 0000000000..9cdd566409 --- /dev/null +++ b/system/ui/lib/text_measure.py @@ -0,0 +1,13 @@ +import pyray as rl + +_cache: dict[int, rl.Vector2] = {} + + +def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = 0) -> rl.Vector2: + key = hash((font.texture.id, text, font_size, spacing)) + if key in _cache: + return _cache[key] + + result = rl.measure_text_ex(font, text, font_size, spacing) + _cache[key] = result + return result From 2e41d959ac8610f2a91d28627c0f1357b1089453 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 4 Jun 2025 14:12:36 +0800 Subject: [PATCH 435/477] ui: add main UI entry point (#35422) * add new main UI entry point * cleanup * mv to selfdrive/ui * fix imports * handle_mouse_click * use ui_state * remove ui_state from gui_app * setup callbacks * handle clicks * put layouts in a dict * update state in render * rebase master * implement settings sidebar * rename files --- selfdrive/ui/layouts/__init__.py | 0 selfdrive/ui/layouts/home.py | 17 ++ selfdrive/ui/layouts/main.py | 92 +++++++++ selfdrive/ui/layouts/settings/settings.py | 174 +++++++++++++++++ selfdrive/ui/layouts/sidebar.py | 207 +++++++++++++++++++++ selfdrive/ui/onroad/augmented_road_view.py | 2 +- selfdrive/ui/ui.py | 20 ++ 7 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 selfdrive/ui/layouts/__init__.py create mode 100644 selfdrive/ui/layouts/home.py create mode 100644 selfdrive/ui/layouts/main.py create mode 100644 selfdrive/ui/layouts/settings/settings.py create mode 100644 selfdrive/ui/layouts/sidebar.py create mode 100755 selfdrive/ui/ui.py diff --git a/selfdrive/ui/layouts/__init__.py b/selfdrive/ui/layouts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py new file mode 100644 index 0000000000..56df253b52 --- /dev/null +++ b/selfdrive/ui/layouts/home.py @@ -0,0 +1,17 @@ +import pyray as rl +from openpilot.system.ui.lib.label import gui_text_box + + +class HomeLayout: + def __init__(self): + pass + + def render(self, rect: rl.Rectangle): + gui_text_box( + rect, + "Demo Home Layout", + font_size=170, + color=rl.WHITE, + alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + ) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py new file mode 100644 index 0000000000..2096480f22 --- /dev/null +++ b/selfdrive/ui/layouts/main.py @@ -0,0 +1,92 @@ +import pyray as rl +from enum import IntEnum +from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH +from openpilot.selfdrive.ui.layouts.home import HomeLayout +from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView + + +class MainState(IntEnum): + HOME = 0 + SETTINGS = 1 + ONROAD = 2 + + +class MainLayout: + def __init__(self): + self._sidebar = Sidebar() + self._sidebar_visible = True + self._current_mode = MainState.HOME + self._prev_onroad = False + self._window_rect = None + self._current_callback: callable | None = None + + # Initialize layouts + self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} + + self._sidebar_rect = rl.Rectangle(0, 0, 0, 0) + self._content_rect = rl.Rectangle(0, 0, 0, 0) + + # Set callbacks + self._setup_callbacks() + + def render(self, rect): + self._current_callback = None + + self._update_layout_rects(rect) + self._render_main_content() + self._handle_input() + + if self._current_callback: + self._current_callback() + + def _setup_callbacks(self): + self._sidebar.set_callbacks( + on_settings=lambda: setattr(self, '_current_callback', self._on_settings_clicked), + on_flag=lambda: setattr(self, '_current_callback', self._on_flag_clicked), + ) + self._layouts[MainState.SETTINGS].set_callbacks( + on_close=lambda: setattr(self, '_current_callback', self._on_settings_closed) + ) + + def _update_layout_rects(self, rect): + self._window_rect = rect + self._sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height) + + x_offset = SIDEBAR_WIDTH if self._sidebar_visible else 0 + self._content_rect = rl.Rectangle(rect.y + x_offset, rect.y, rect.width - x_offset, rect.height) + + def _on_settings_clicked(self): + self._current_mode = MainState.SETTINGS + self._sidebar_visible = False + + def _on_settings_closed(self): + self._current_mode = MainState.HOME if not ui_state.started else MainState.ONROAD + self._sidebar_visible = True + + def _on_flag_clicked(self): + pass + + def _render_main_content(self): + # Render sidebar + if self._sidebar_visible: + self._sidebar.render(self._sidebar_rect) + + if ui_state.started != self._prev_onroad: + self._prev_onroad = ui_state.started + if ui_state.started: + self._current_mode = MainState.ONROAD + else: + self._current_mode = MainState.HOME + + content_rect = self._content_rect if self._sidebar_visible else self._window_rect + self._layouts[self._current_mode].render(content_rect) + + def _handle_input(self): + if self._current_mode != MainState.ONROAD or not rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + return + + mouse_pos = rl.get_mouse_position() + if rl.check_collision_point_rec(mouse_pos, self._content_rect): + self._sidebar_visible = not self._sidebar_visible diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py new file mode 100644 index 0000000000..6a9a50b855 --- /dev/null +++ b/selfdrive/ui/layouts/settings/settings.py @@ -0,0 +1,174 @@ +import pyray as rl +from dataclasses import dataclass +from enum import IntEnum +from collections.abc import Callable +from openpilot.common.params import Params +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.label import gui_text_box + +# Import individual panels + +SETTINGS_CLOSE_TEXT = "X" +# Constants +SIDEBAR_WIDTH = 500 +CLOSE_BTN_SIZE = 200 +NAV_BTN_HEIGHT = 80 +PANEL_MARGIN = 50 +SCROLL_SPEED = 30 + +# Colors +SIDEBAR_COLOR = rl.BLACK +PANEL_COLOR = rl.Color(41, 41, 41, 255) +CLOSE_BTN_COLOR = rl.Color(41, 41, 41, 255) +CLOSE_BTN_PRESSED = rl.Color(59, 59, 59, 255) +TEXT_NORMAL = rl.Color(128, 128, 128, 255) +TEXT_SELECTED = rl.Color(255, 255, 255, 255) +TEXT_PRESSED = rl.Color(173, 173, 173, 255) + + +class PanelType(IntEnum): + DEVICE = 0 + NETWORK = 1 + TOGGLES = 2 + SOFTWARE = 3 + FIREHOSE = 4 + DEVELOPER = 5 + + +@dataclass +class PanelInfo: + name: str + instance: object + button_rect: rl.Rectangle + + +class SettingsLayout: + def __init__(self): + self._params = Params() + self._current_panel = PanelType.DEVICE + self._close_btn_pressed = False + self._scroll_offset = 0.0 + self._max_scroll = 0.0 + + # Panel configuration + self._panels = { + PanelType.DEVICE: PanelInfo("Device", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.TOGGLES: PanelInfo("Toggles", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.SOFTWARE: PanelInfo("Software", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.FIREHOSE: PanelInfo("Firehose", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.NETWORK: PanelInfo("Network", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.DEVELOPER: PanelInfo("Developer", None, rl.Rectangle(0, 0, 0, 0)), + } + + self._font_medium = gui_app.font(FontWeight.MEDIUM) + self._font_bold = gui_app.font(FontWeight.SEMI_BOLD) + + # Callbacks + self._close_callback: Callable | None = None + + def set_callbacks(self, on_close: Callable): + self._close_callback = on_close + + def render(self, rect: rl.Rectangle): + # Calculate layout + sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height) + panel_rect = rl.Rectangle(rect.x + SIDEBAR_WIDTH, rect.y, rect.width - SIDEBAR_WIDTH, rect.height) + + # Draw components + self._draw_sidebar(sidebar_rect) + self._draw_current_panel(panel_rect) + + if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): + self.handle_mouse_release(rl.get_mouse_position()) + + def _draw_sidebar(self, rect: rl.Rectangle): + rl.draw_rectangle_rec(rect, SIDEBAR_COLOR) + + # Close button + close_btn_rect = rl.Rectangle( + rect.x + (rect.width - CLOSE_BTN_SIZE) / 2, rect.y + 45, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE + ) + + close_color = CLOSE_BTN_PRESSED if self._close_btn_pressed else CLOSE_BTN_COLOR + rl.draw_rectangle_rounded(close_btn_rect, 0.5, 20, close_color) + close_text_size = rl.measure_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, 140, 0) + close_text_pos = rl.Vector2( + close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2, + close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2 - 20, + ) + rl.draw_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, close_text_pos, 140, 0, TEXT_SELECTED) + + # Store close button rect for click detection + self._close_btn_rect = close_btn_rect + + # Navigation buttons + nav_start_y = rect.y + 300 + button_spacing = 20 + + i = 0 + for panel_type, panel_info in self._panels.items(): + button_rect = rl.Rectangle( + rect.x + 50, + nav_start_y + i * (NAV_BTN_HEIGHT + button_spacing), + rect.width - 150, # Right-aligned with margin + NAV_BTN_HEIGHT, + ) + + # Button styling + is_selected = panel_type == self._current_panel + text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL + + # Draw button text (right-aligned) + text_size = rl.measure_text_ex(self._font_medium, panel_info.name, 65, 0) + text_pos = rl.Vector2( + button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2 + ) + rl.draw_text_ex(self._font_medium, panel_info.name, text_pos, 65, 0, text_color) + + # Store button rect for click detection + panel_info.button_rect = button_rect + i += 1 + + def _draw_current_panel(self, rect: rl.Rectangle): + content_rect = rl.Rectangle(rect.x + PANEL_MARGIN, rect.y + 25, rect.width - (PANEL_MARGIN * 2), rect.height - 50) + rl.draw_rectangle_rounded(content_rect, 0.03, 30, PANEL_COLOR) + gui_text_box( + content_rect, + f"Demo {self._panels[self._current_panel].name} Panel", + font_size=170, + color=rl.WHITE, + alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + ) + + def handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool: + # Check close button + if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect): + self._close_btn_pressed = True + if self._close_callback: + self._close_callback() + return True + + # Check navigation buttons + for panel_type, panel_info in self._panels.items(): + if rl.check_collision_point_rec(mouse_pos, panel_info.button_rect): + self._switch_to_panel(panel_type) + return True + + return False + + def _switch_to_panel(self, panel_type: PanelType): + if panel_type != self._current_panel: + self._current_panel = panel_type + self._scroll_offset = 0.0 # Reset scroll when switching panels + self._transition_progress = 0.0 + self._transitioning = True + + def set_current_panel(self, index: int, param: str = ""): + panel_types = list(self._panels.keys()) + if 0 <= index < len(panel_types): + self._switch_to_panel(panel_types[index]) + + def close_settings(self): + if self._close_callback: + self._close_callback() diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py new file mode 100644 index 0000000000..eeb073e67b --- /dev/null +++ b/selfdrive/ui/layouts/sidebar.py @@ -0,0 +1,207 @@ +import pyray as rl +import time +from dataclasses import dataclass +from collections.abc import Callable +from cereal import log +from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.system.ui.lib.application import gui_app, FontWeight + +SIDEBAR_WIDTH = 300 +METRIC_HEIGHT = 126 +METRIC_WIDTH = 240 +METRIC_MARGIN = 30 + +SETTINGS_BTN = rl.Rectangle(50, 35, 200, 117) +HOME_BTN = rl.Rectangle(60, 860, 180, 180) + +ThermalStatus = log.DeviceState.ThermalStatus +NetworkType = log.DeviceState.NetworkType + +# Color scheme +class Colors: + SIDEBAR_BG = rl.Color(57, 57, 57, 255) + WHITE = rl.Color(255, 255, 255, 255) + WHITE_DIM = rl.Color(255, 255, 255, 85) + GRAY = rl.Color(84, 84, 84, 255) + + # Status colors + GOOD = rl.Color(255, 255, 255, 255) + WARNING = rl.Color(218, 202, 37, 255) + DANGER = rl.Color(201, 34, 49, 255) + + # UI elements + METRIC_BORDER = rl.Color(255, 255, 255, 85) + BUTTON_NORMAL = rl.Color(255, 255, 255, 255) + BUTTON_PRESSED = rl.Color(255, 255, 255, 166) + +NETWORK_TYPES = { + NetworkType.none: "Offline", + NetworkType.wifi: "WiFi", + NetworkType.cell2G: "2G", + NetworkType.cell3G: "3G", + NetworkType.cell4G: "LTE", + NetworkType.cell5G: "5G", + NetworkType.ethernet: "Ethernet", +} + + +@dataclass(slots=True) +class MetricData: + label: str + value: str + color: rl.Color + + def update(self, label: str, value: str, color: rl.Color): + self.label = label + self.value = value + self.color = color + +class Sidebar: + def __init__(self): + self._net_type = NETWORK_TYPES.get(NetworkType.none) + self._net_strength = 0 + + self._temp_status = MetricData("TEMP", "GOOD", Colors.GOOD) + self._panda_status = MetricData("VEHICLE", "ONLINE", Colors.GOOD) + self._connect_status = MetricData("CONNECT", "OFFLINE", Colors.WARNING) + + self._home_img = gui_app.texture("images/button_home.png", HOME_BTN.width, HOME_BTN.height) + self._flag_img = gui_app.texture("images/button_flag.png", HOME_BTN.width, HOME_BTN.height) + self._settings_img = gui_app.texture("images/button_settings.png", SETTINGS_BTN.width, SETTINGS_BTN.height) + self._font_regular = gui_app.font(FontWeight.NORMAL) + self._font_bold = gui_app.font(FontWeight.SEMI_BOLD) + + # Callbacks + self._on_settings_click: Callable | None = None + self._on_flag_click: Callable | None = None + + def set_callbacks(self, on_settings: Callable | None = None, on_flag: Callable | None = None): + self._on_settings_click = on_settings + self._on_flag_click = on_flag + + def render(self, rect: rl.Rectangle): + self.update_state() + + # Background + rl.draw_rectangle_rec(rect, Colors.SIDEBAR_BG) + + self._draw_buttons(rect) + self._draw_network_indicator(rect) + self._draw_metrics(rect) + + self._handle_mouse_release() + + def update_state(self): + sm = ui_state.sm + if not sm.updated['deviceState']: + return + + device_state = sm['deviceState'] + + self._update_network_status(device_state) + self._update_temperature_status(device_state) + self._update_connection_status(device_state) + self._update_panda_status() + + def _update_network_status(self, device_state): + self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, "Unknown") + strength = device_state.networkStrength + self._net_strength = max(0, min(5, strength.raw + 1)) if strength > 0 else 0 + + def _update_temperature_status(self, device_state): + thermal_status = device_state.thermalStatus + + if thermal_status == ThermalStatus.green: + self._temp_status.update("TEMP", "GOOD", Colors.GOOD) + elif thermal_status == ThermalStatus.yellow: + self._temp_status.update("TEMP", "OK", Colors.WARNING) + else: + self._temp_status.update("TEMP", "HIGH", Colors.DANGER) + + def _update_connection_status(self, device_state): + last_ping = device_state.lastAthenaPingTime + if last_ping == 0: + self._connect_status.update("CONNECT", "OFFLINE", Colors.WARNING) + elif time.monotonic_ns() - last_ping < 80_000_000_000: # 80 seconds in nanoseconds + self._connect_status.update("CONNECT", "ONLINE", Colors.GOOD) + else: + self._connect_status.update("CONNECT", "ERROR", Colors.DANGER) + + def _update_panda_status(self): + if ui_state.panda_type == log.PandaState.PandaType.unknown: + self._panda_status.update("NO", "PANDA", Colors.DANGER) + else: + self._panda_status.update("VEHICLE", "ONLINE", Colors.GOOD) + + def _handle_mouse_release(self): + if not rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): + return + + mouse_pos = rl.get_mouse_position() + if rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN): + if self._on_settings_click: + self._on_settings_click() + elif rl.check_collision_point_rec(mouse_pos, HOME_BTN) and ui_state.started: + if self._on_flag_click: + self._on_flag_click() + + def _draw_buttons(self, rect: rl.Rectangle): + mouse_pos = rl.get_mouse_position() + mouse_down = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) + + + # Settings button + settings_down = mouse_down and rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN) + tint = Colors.BUTTON_PRESSED if settings_down else Colors.BUTTON_NORMAL + rl.draw_texture(self._settings_img, int(SETTINGS_BTN.x), int(SETTINGS_BTN.y), tint) + + # Home/Flag button + flag_pressed = mouse_down and rl.check_collision_point_rec(mouse_pos, HOME_BTN) + button_img = self._flag_img if ui_state.started else self._home_img + + tint = Colors.BUTTON_PRESSED if (ui_state.started and flag_pressed) else Colors.BUTTON_NORMAL + rl.draw_texture(button_img, int(HOME_BTN.x), int(HOME_BTN.y), tint) + + def _draw_network_indicator(self, rect: rl.Rectangle): + # Signal strength dots + x_start = rect.x + 58 + y_pos = rect.y + 196 + dot_size = 27 + dot_spacing = 37 + + for i in range(5): + color = Colors.WHITE if i < self._net_strength else Colors.GRAY + x = int(x_start + i * dot_spacing + dot_size // 2) + y = int(y_pos + dot_size // 2) + rl.draw_circle(x, y, dot_size // 2, color) + + # Network type text + text_y = rect.y + 247 + text_pos = rl.Vector2(rect.x + 58, text_y) + rl.draw_text_ex(self._font_regular, self._net_type, text_pos, 35, 0, Colors.WHITE) + + def _draw_metrics(self, rect: rl.Rectangle): + metrics = [(self._temp_status, 338), (self._panda_status, 496), (self._connect_status, 654)] + + for metric, y_offset in metrics: + self._draw_metric(rect, metric, rect.y + y_offset) + + def _draw_metric(self, rect: rl.Rectangle, metric: MetricData, y: float): + metric_rect = rl.Rectangle(rect.x + METRIC_MARGIN, y, METRIC_WIDTH, METRIC_HEIGHT) + # Draw colored left edge (clipped rounded rectangle) + edge_rect = rl.Rectangle(metric_rect.x + 4, metric_rect.y + 4, 100, 118) + rl.begin_scissor_mode(int(metric_rect.x + 4), int(metric_rect.y), 18, int(metric_rect.height)) + rl.draw_rectangle_rounded(edge_rect, 0.18, 10, metric.color) + rl.end_scissor_mode() + + # Draw border + rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.15, 10, 2, Colors.METRIC_BORDER) + + # Draw text + text = f"{metric.label}\n{metric.value}" + text_size = rl.measure_text_ex(self._font_bold, text, 35, 0) + text_pos = rl.Vector2( + metric_rect.x + 22 + (metric_rect.width - 22 - text_size.x) / 2, + metric_rect.y + (metric_rect.height - text_size.y) / 2 + ) + rl.draw_text_ex(self._font_bold, text, text_pos, 35, 0, Colors.WHITE) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index f987d70c9c..fa7469d2dd 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -26,7 +26,7 @@ BORDER_COLORS = { class AugmentedRoadView(CameraView): - def __init__(self, stream_type: VisionStreamType): + def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD): super().__init__("camerad", stream_type) self.device_camera: DeviceCameraConfig | None = None diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py new file mode 100755 index 0000000000..8a3e34466f --- /dev/null +++ b/selfdrive/ui/ui.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import pyray as rl +from openpilot.system.ui.lib.application import gui_app +from openpilot.selfdrive.ui.layouts.main import MainLayout +from openpilot.selfdrive.ui.ui_state import ui_state + + +def main(): + gui_app.init_window("UI") + main_layout = MainLayout() + for _ in gui_app.render(): + ui_state.update() + + #TODO handle brigntness and awake state here + + main_layout.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) + + +if __name__ == "__main__": + main() From 912160c429903c80664208b89de980b0204331d0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 5 Jun 2025 04:54:45 +0800 Subject: [PATCH 436/477] ui: improve calibration height initialization (#35459) improve calibration height initialization --- selfdrive/ui/onroad/model_renderer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 38951fb504..ad037f8af3 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -7,6 +7,7 @@ from openpilot.common.params import Params from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.shader_polygon import draw_polygon +from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT CLIP_MARGIN = 500 @@ -51,7 +52,7 @@ class ModelRenderer: self._lane_line_probs = np.zeros(4, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32) self._lead_vehicles = [LeadVehicle(), LeadVehicle()] - self._path_offset_z = 1.22 + self._path_offset_z = HEIGHT_INIT[0] # Initialize ModelPoints objects self._path = ModelPoints() @@ -99,7 +100,10 @@ class ModelRenderer: # Update state self._experimental_mode = sm['selfdriveState'].experimentalMode - self._path_offset_z = sm['liveCalibration'].height[0] + + live_calib = sm['liveCalibration'] + self._path_offset_z = live_calib.height[0] if live_calib.height else HEIGHT_INIT[0] + if sm.updated['carParams']: self._longitudinal_control = sm['carParams'].openpilotLongitudinalControl From 88466fb62f0530813e8823132883e458c24ce164 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 5 Jun 2025 04:55:03 +0800 Subject: [PATCH 437/477] ui: improve onroad layout transitions (#35458) improve onroad layout transitions --- selfdrive/ui/layouts/main.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 2096480f22..08ce50407d 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -35,6 +35,7 @@ class MainLayout: self._current_callback = None self._update_layout_rects(rect) + self._handle_onroad_transition() self._render_main_content() self._handle_input() @@ -47,7 +48,7 @@ class MainLayout: on_flag=lambda: setattr(self, '_current_callback', self._on_flag_clicked), ) self._layouts[MainState.SETTINGS].set_callbacks( - on_close=lambda: setattr(self, '_current_callback', self._on_settings_closed) + on_close=lambda: setattr(self, '_current_callback', self._set_mode_for_state) ) def _update_layout_rects(self, rect): @@ -57,14 +58,24 @@ class MainLayout: x_offset = SIDEBAR_WIDTH if self._sidebar_visible else 0 self._content_rect = rl.Rectangle(rect.y + x_offset, rect.y, rect.width - x_offset, rect.height) + def _handle_onroad_transition(self): + if ui_state.started != self._prev_onroad: + self._prev_onroad = ui_state.started + + self._set_mode_for_state() + + def _set_mode_for_state(self): + if ui_state.started: + self._current_mode = MainState.ONROAD + self._sidebar_visible = False + else: + self._current_mode = MainState.HOME + self._sidebar_visible = True + def _on_settings_clicked(self): self._current_mode = MainState.SETTINGS self._sidebar_visible = False - def _on_settings_closed(self): - self._current_mode = MainState.HOME if not ui_state.started else MainState.ONROAD - self._sidebar_visible = True - def _on_flag_clicked(self): pass @@ -73,13 +84,6 @@ class MainLayout: if self._sidebar_visible: self._sidebar.render(self._sidebar_rect) - if ui_state.started != self._prev_onroad: - self._prev_onroad = ui_state.started - if ui_state.started: - self._current_mode = MainState.ONROAD - else: - self._current_mode = MainState.HOME - content_rect = self._content_rect if self._sidebar_visible else self._window_rect self._layouts[self._current_mode].render(content_rect) From 96cfd5aaf7746e965e7f19850de13881c644d8d5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 5 Jun 2025 05:06:50 +0800 Subject: [PATCH 438/477] ui: add ListView component and settings layouts with declarative UI (#35453) * add flexible ListView component * fix crash --------- Co-authored-by: Shane Smiskol --- selfdrive/ui/layouts/settings/developer.py | 52 +++ selfdrive/ui/layouts/settings/device.py | 47 +++ selfdrive/ui/layouts/settings/settings.py | 37 +- selfdrive/ui/layouts/settings/software.py | 21 ++ selfdrive/ui/layouts/settings/toggles.py | 68 ++++ system/ui/lib/list_view.py | 380 +++++++++++++++++++++ system/ui/lib/toggle.py | 24 +- system/ui/lib/wrap_text.py | 87 +++++ 8 files changed, 690 insertions(+), 26 deletions(-) create mode 100644 selfdrive/ui/layouts/settings/developer.py create mode 100644 selfdrive/ui/layouts/settings/device.py create mode 100644 selfdrive/ui/layouts/settings/software.py create mode 100644 selfdrive/ui/layouts/settings/toggles.py create mode 100644 system/ui/lib/list_view.py create mode 100644 system/ui/lib/wrap_text.py diff --git a/selfdrive/ui/layouts/settings/developer.py b/selfdrive/ui/layouts/settings/developer.py new file mode 100644 index 0000000000..6ab5be2792 --- /dev/null +++ b/selfdrive/ui/layouts/settings/developer.py @@ -0,0 +1,52 @@ +from openpilot.system.ui.lib.list_view import ListView, toggle_item +from openpilot.common.params import Params + +# Description constants +DESCRIPTIONS = { + 'enable_adb': ( + "ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " + + "See https://docs.comma.ai/how-to/connect-to-comma for more info." + ), + 'joystick_debug_mode': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", +} + + +class DeveloperLayout: + def __init__(self): + self._params = Params() + items = [ + toggle_item( + "Enable ADB", + description=DESCRIPTIONS["enable_adb"], + initial_state=self._params.get_bool("AdbEnabled"), + callback=self._on_enable_adb, + ), + toggle_item( + "Joystick Debug Mode", + description=DESCRIPTIONS["joystick_debug_mode"], + initial_state=self._params.get_bool("JoystickDebugMode"), + callback=self._on_joystick_debug_mode, + ), + toggle_item( + "Longitudinal Maneuver Mode", + description="", + initial_state=self._params.get_bool("LongitudinalManeuverMode"), + callback=self._on_long_maneuver_mode, + ), + toggle_item( + "openpilot Longitudinal Control (Alpha)", + description="", + initial_state=self._params.get_bool("AlphaLongitudinalEnabled"), + callback=self._on_alpha_long_enabled, + ), + ] + + self._list_widget = ListView(items) + + def render(self, rect): + self._list_widget.render(rect) + + def _on_enable_adb(self): pass + def _on_joystick_debug_mode(self): pass + def _on_long_maneuver_mode(self): pass + def _on_alpha_long_enabled(self): pass diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py new file mode 100644 index 0000000000..5051dd6ba6 --- /dev/null +++ b/selfdrive/ui/layouts/settings/device.py @@ -0,0 +1,47 @@ +from openpilot.system.ui.lib.list_view import ListView, text_item, button_item +from openpilot.common.params import Params +from openpilot.system.hardware import TICI + +# Description constants +DESCRIPTIONS = { + 'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.", + 'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", + 'reset_calibration': ( + "openpilot requires the device to be mounted within 4° left or right and within 5° " + + "up or 9° down. openpilot is continuously calibrating, resetting is rarely required." + ), + 'review_guide': "Review the rules, features, and limitations of openpilot", +} + + +class DeviceLayout: + def __init__(self): + params = Params() + dongle_id = params.get("DongleId", encoding="utf-8") or "N/A" + serial = params.get("HardwareSerial") or "N/A" + + items = [ + text_item("Dongle ID", dongle_id), + text_item("Serial", serial), + button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], self._on_pair_device), + button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], self._on_driver_camera), + button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], self._on_reset_calibration), + button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), + ] + + if TICI: + items.append(button_item("Regulatory", "VIEW", callback=self._on_regulatory)) + + items.append(button_item("Change Language", "CHANGE", callback=self._on_change_language)) + + self._list_widget = ListView(items) + + def render(self, rect): + self._list_widget.render(rect) + + def _on_pair_device(self): pass + def _on_driver_camera(self): pass + def _on_reset_calibration(self): pass + def _on_review_training_guide(self): pass + def _on_regulatory(self): pass + def _on_change_language(self): pass diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 6a9a50b855..fc03529e56 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -3,6 +3,10 @@ from dataclasses import dataclass from enum import IntEnum from collections.abc import Callable from openpilot.common.params import Params +from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout +from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout +from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout +from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_text_box @@ -52,12 +56,12 @@ class SettingsLayout: # Panel configuration self._panels = { - PanelType.DEVICE: PanelInfo("Device", None, rl.Rectangle(0, 0, 0, 0)), - PanelType.TOGGLES: PanelInfo("Toggles", None, rl.Rectangle(0, 0, 0, 0)), - PanelType.SOFTWARE: PanelInfo("Software", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.DEVICE: PanelInfo("Device", DeviceLayout(), rl.Rectangle(0, 0, 0, 0)), + PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout(), rl.Rectangle(0, 0, 0, 0)), + PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout(), rl.Rectangle(0, 0, 0, 0)), PanelType.FIREHOSE: PanelInfo("Firehose", None, rl.Rectangle(0, 0, 0, 0)), PanelType.NETWORK: PanelInfo("Network", None, rl.Rectangle(0, 0, 0, 0)), - PanelType.DEVELOPER: PanelInfo("Developer", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout(), rl.Rectangle(0, 0, 0, 0)), } self._font_medium = gui_app.font(FontWeight.MEDIUM) @@ -130,16 +134,23 @@ class SettingsLayout: i += 1 def _draw_current_panel(self, rect: rl.Rectangle): - content_rect = rl.Rectangle(rect.x + PANEL_MARGIN, rect.y + 25, rect.width - (PANEL_MARGIN * 2), rect.height - 50) - rl.draw_rectangle_rounded(content_rect, 0.03, 30, PANEL_COLOR) - gui_text_box( - content_rect, - f"Demo {self._panels[self._current_panel].name} Panel", - font_size=170, - color=rl.WHITE, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, - alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + rl.draw_rectangle_rounded( + rl.Rectangle(rect.x + 10, rect.y + 10, rect.width - 20, rect.height - 20), 0.04, 30, PANEL_COLOR ) + content_rect = rl.Rectangle(rect.x + PANEL_MARGIN, rect.y + 25, rect.width - (PANEL_MARGIN * 2), rect.height - 50) + # rl.draw_rectangle_rounded(content_rect, 0.03, 30, PANEL_COLOR) + panel = self._panels[self._current_panel] + if panel.instance: + panel.instance.render(content_rect) + else: + gui_text_box( + content_rect, + f"Demo {self._panels[self._current_panel].name} Panel", + font_size=170, + color=rl.WHITE, + alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, + alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + ) def handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool: # Check close button diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py new file mode 100644 index 0000000000..e57e13c148 --- /dev/null +++ b/selfdrive/ui/layouts/settings/software.py @@ -0,0 +1,21 @@ +from openpilot.system.ui.lib.list_view import ListView, button_item, text_item + +class SoftwareLayout: + def __init__(self): + items = [ + text_item("Current Version", ""), + button_item("Download", "CHECK", callback=self._on_download_update), + button_item("Install Update", "INSTALL", callback=self._on_install_update), + button_item("Target Branch", "SELECT", callback=self._on_select_branch), + button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall), + ] + + self._list_widget = ListView(items) + + def render(self, rect): + self._list_widget.render(rect) + + def _on_download_update(self): pass + def _on_install_update(self): pass + def _on_select_branch(self): pass + def _on_uninstall(self): pass diff --git a/selfdrive/ui/layouts/settings/toggles.py b/selfdrive/ui/layouts/settings/toggles.py new file mode 100644 index 0000000000..d966baf9cd --- /dev/null +++ b/selfdrive/ui/layouts/settings/toggles.py @@ -0,0 +1,68 @@ +from openpilot.system.ui.lib.list_view import ListView, toggle_item +from openpilot.common.params import Params + +# Description constants +DESCRIPTIONS = { + "OpenpilotEnabledToggle": ( + "Use the openpilot system for adaptive cruise control and lane keep driver assistance. " + + "Your attention is required at all times to use this feature." + ), + "DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.", + "IsLdwEnabled": ( + "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " + + "without a turn signal activated while driving over 31 mph (50 km/h)." + ), + "AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.", + 'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", + "IsMetric": "Display speed in km/h instead of mph.", +} + + +class TogglesLayout: + def __init__(self): + self._params = Params() + items = [ + toggle_item( + "Enable openpilot", + DESCRIPTIONS["OpenpilotEnabledToggle"], + self._params.get_bool("OpenpilotEnabledToggle"), + icon="chffr_wheel.png", + ), + toggle_item( + "Experimental Mode", + initial_state=self._params.get_bool("ExperimentalMode"), + icon="experimental_white.png", + ), + toggle_item( + "Disengage on Accelerator Pedal", + DESCRIPTIONS["DisengageOnAccelerator"], + self._params.get_bool("DisengageOnAccelerator"), + icon="disengage_on_accelerator.png", + ), + toggle_item( + "Enable Lane Departure Warnings", + DESCRIPTIONS["IsLdwEnabled"], + self._params.get_bool("IsLdwEnabled"), + icon="warning.png", + ), + toggle_item( + "Always-On Driver Monitoring", + DESCRIPTIONS["AlwaysOnDM"], + self._params.get_bool("AlwaysOnDM"), + icon="monitoring.png", + ), + toggle_item( + "Record and Upload Driver Camera", + DESCRIPTIONS["RecordFront"], + self._params.get_bool("RecordFront"), + icon="monitoring.png", + ), + toggle_item( + "Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="monitoring.png" + ), + ] + + self._list_widget = ListView(items) + + def render(self, rect): + self._list_widget.render(rect) diff --git a/system/ui/lib/list_view.py b/system/ui/lib/list_view.py new file mode 100644 index 0000000000..9ca2363bc7 --- /dev/null +++ b/system/ui/lib/list_view.py @@ -0,0 +1,380 @@ +import os +import pyray as rl +from dataclasses import dataclass +from collections.abc import Callable +from abc import ABC, abstractmethod +from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.wrap_text import wrap_text +from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.toggle import Toggle +from openpilot.system.ui.lib.toggle import WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT + + +LINE_PADDING = 40 +LINE_COLOR = rl.GRAY +ITEM_PADDING = 20 +ITEM_SPACING = 80 +ITEM_BASE_HEIGHT = 170 +ITEM_TEXT_FONT_SIZE = 50 +ITEM_TEXT_COLOR = rl.WHITE +ITEM_DESC_TEXT_COLOR = rl.Color(128, 128, 128, 255) +ITEM_DESC_FONT_SIZE = 40 +ITEM_DESC_V_OFFSET = 130 +RIGHT_ITEM_PADDING = 20 +ICON_SIZE = 80 +BUTTON_WIDTH = 250 +BUTTON_HEIGHT = 100 +BUTTON_BORDER_RADIUS = 50 +BUTTON_FONT_SIZE = 35 +BUTTON_FONT_WEIGHT = FontWeight.MEDIUM + + +# Abstract base class for right-side items +class RightItem(ABC): + def __init__(self, width: int = 100): + self.width = width + self.enabled = True + + @abstractmethod + def draw(self, rect: rl.Rectangle) -> bool: + pass + + @abstractmethod + def get_width(self) -> int: + pass + + +class ToggleRightItem(RightItem): + def __init__(self, initial_state: bool = False, width: int = TOGGLE_WIDTH): + super().__init__(width) + self.toggle = Toggle(initial_state=initial_state) + self.state = initial_state + self.enabled = True + + def draw(self, rect: rl.Rectangle) -> bool: + if self.toggle.render(rl.Rectangle(rect.x, rect.y + (rect.height - TOGGLE_HEIGHT) / 2, self.width, TOGGLE_HEIGHT)): + self.state = not self.state + return True + return False + + def get_width(self) -> int: + return self.width + + def set_state(self, state: bool): + self.state = state + self.toggle.set_state(state) + + def get_state(self) -> bool: + return self.state + + def set_enabled(self, enabled: bool): + self.enabled = enabled + + +class ButtonRightItem(RightItem): + def __init__(self, text: str, width: int = BUTTON_WIDTH): + super().__init__(width) + self.text = text + self.enabled = True + + def draw(self, rect: rl.Rectangle) -> bool: + return ( + gui_button( + rl.Rectangle(rect.x, rect.y + (rect.height - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT), + self.text, + border_radius=BUTTON_BORDER_RADIUS, + font_weight=BUTTON_FONT_WEIGHT, + font_size=BUTTON_FONT_SIZE, + is_enabled=self.enabled, + ) + == 1 + ) + + def get_width(self) -> int: + return self.width + + def set_enabled(self, enabled: bool): + self.enabled = enabled + + +class TextRightItem(RightItem): + def __init__(self, text: str, color: rl.Color = ITEM_TEXT_COLOR, font_size: int = ITEM_TEXT_FONT_SIZE): + self.text = text + self.color = color + self.font_size = font_size + + font = gui_app.font(FontWeight.NORMAL) + text_width = measure_text_cached(font, text, font_size).x + super().__init__(int(text_width + 20)) + + def draw(self, rect: rl.Rectangle) -> bool: + font = gui_app.font(FontWeight.NORMAL) + text_size = measure_text_cached(font, self.text, self.font_size) + + # Center the text in the allocated rectangle + text_x = rect.x + (rect.width - text_size.x) / 2 + text_y = rect.y + (rect.height - text_size.y) / 2 + + rl.draw_text_ex(font, self.text, rl.Vector2(text_x, text_y), self.font_size, 0, self.color) + return False + + def get_width(self) -> int: + return self.width + + def set_text(self, text: str): + self.text = text + font = gui_app.font(FontWeight.NORMAL) + text_width = measure_text_cached(font, text, self.font_size).x + self.width = int(text_width + 20) + + +@dataclass +class ListItem: + title: str + icon: str | None = None + description: str | None = None + description_visible: bool = False + rect: "rl.Rectangle | None" = None + callback: Callable | None = None + right_item: RightItem | None = None + + # Cached properties for performance + _wrapped_description: str | None = None + _description_height: float = 0 + + def get_right_item(self) -> RightItem | None: + return self.right_item + + def get_item_height(self, font: rl.Font, max_width: int) -> float: + if self.description_visible and self.description: + if not self._wrapped_description: + wrapped_lines = wrap_text(font, self.description, ITEM_DESC_FONT_SIZE, max_width) + self._wrapped_description = "\n".join(wrapped_lines) + self._description_height = len(wrapped_lines) * 20 + 10 # Line height + padding + return ITEM_BASE_HEIGHT + self._description_height - (ITEM_BASE_HEIGHT - ITEM_DESC_V_OFFSET) + ITEM_SPACING + return ITEM_BASE_HEIGHT + + def get_content_width(self, total_width: int) -> int: + if self.right_item: + return total_width - self.right_item.get_width() - RIGHT_ITEM_PADDING + return total_width + + def get_right_item_rect(self, item_rect: rl.Rectangle) -> rl.Rectangle: + if not self.right_item: + return rl.Rectangle(0, 0, 0, 0) + + right_width = self.right_item.get_width() + right_x = item_rect.x + item_rect.width - right_width + right_y = item_rect.y + return rl.Rectangle(right_x, right_y, right_width, ITEM_BASE_HEIGHT) + + +class ListView: + def __init__(self, items: list[ListItem]): + self._items: list[ListItem] = items + self._last_dim: tuple[float, float] = (0, 0) + self.scroll_panel = GuiScrollPanel() + + self._font_normal = gui_app.font(FontWeight.NORMAL) + + # Interaction state + self._hovered_item: int = -1 + self._last_mouse_pos = rl.Vector2(0, 0) + + self._total_height: float = 0 + self._visible_range = (0, 0) + + def invalid_height_cache(self): + self._last_dim = (0, 0) + + def render(self, rect: rl.Rectangle): + if self._last_dim != (rect.width, rect.height): + self._update_item_rects(rect) + self._last_dim = (rect.width, rect.height) + + # Update layout and handle scrolling + content_rect = rl.Rectangle(rect.x, rect.y, rect.width, self._total_height) + scroll_offset = self.scroll_panel.handle_scroll(rect, content_rect) + + # Handle mouse interaction + if self.scroll_panel.is_click_valid(): + self._handle_mouse_interaction(rect, scroll_offset) + + # Set scissor mode for clipping + rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) + + # Calculate visible range for performance + self._calculate_visible_range(rect, -scroll_offset.y) + + # Render only visible items + for i in range(self._visible_range[0], min(self._visible_range[1], len(self._items))): + item = self._items[i] + if item.rect: + adjusted_rect = rl.Rectangle(item.rect.x, item.rect.y + scroll_offset.y, item.rect.width, item.rect.height) + self._render_item(item, adjusted_rect, i) + + if i != len(self._items) - 1: + rl.draw_line_ex( + rl.Vector2(adjusted_rect.x + LINE_PADDING, adjusted_rect.y + adjusted_rect.height - 1), + rl.Vector2( + adjusted_rect.x + adjusted_rect.width - LINE_PADDING * 2, adjusted_rect.y + adjusted_rect.height - 1 + ), + 1.0, + LINE_COLOR, + ) + rl.end_scissor_mode() + + def _render_item(self, item: ListItem, rect: rl.Rectangle, index: int): + content_x = rect.x + ITEM_PADDING + text_x = content_x + + # Calculate available width for main content + content_width = item.get_content_width(int(rect.width - ITEM_PADDING * 2)) + + # Draw icon if present + if item.icon: + icon_texture = gui_app.texture(os.path.join("icons", item.icon), ICON_SIZE, ICON_SIZE) + rl.draw_texture( + icon_texture, int(content_x), int(rect.y + (ITEM_BASE_HEIGHT - icon_texture.width) // 2), rl.WHITE + ) + text_x += ICON_SIZE + ITEM_PADDING + + # Draw main text + text_size = measure_text_cached(self._font_normal, item.title, ITEM_TEXT_FONT_SIZE) + item_y = rect.y + (ITEM_BASE_HEIGHT - text_size.y) // 2 + rl.draw_text_ex(self._font_normal, item.title, rl.Vector2(text_x, item_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_COLOR) + + # Draw description if visible (adjust width for right item) + if item.description_visible and item._wrapped_description: + desc_y = rect.y + ITEM_DESC_V_OFFSET + desc_max_width = int(content_width - (text_x - content_x)) + + # Re-wrap description if needed due to right item + if (item.right_item and item.description) and not item._wrapped_description: + wrapped_lines = wrap_text(self._font_normal, item.description, ITEM_DESC_FONT_SIZE, desc_max_width) + item._wrapped_description = "\n".join(wrapped_lines) + + rl.draw_text_ex( + self._font_normal, + item._wrapped_description, + rl.Vector2(text_x, desc_y), + ITEM_DESC_FONT_SIZE, + 0, + ITEM_DESC_TEXT_COLOR, + ) + + # Draw right item if present + if item.right_item: + right_rect = item.get_right_item_rect(rect) + # Adjust for scroll offset + right_rect.y = right_rect.y + if item.right_item.draw(right_rect): + # Right item was clicked/activated + if item.callback: + item.callback() + + def _update_item_rects(self, container_rect: rl.Rectangle) -> None: + current_y: float = 0.0 + self._total_height = 0 + + for item in self._items: + content_width = item.get_content_width(int(container_rect.width - ITEM_PADDING * 2)) + item_height = item.get_item_height(self._font_normal, content_width) + item.rect = rl.Rectangle(container_rect.x, container_rect.y + current_y, container_rect.width, item_height) + current_y += item_height + self._total_height += item_height + + def _calculate_visible_range(self, rect: rl.Rectangle, scroll_offset: float): + if not self._items: + self._visible_range = (0, 0) + return + + visible_top = scroll_offset + visible_bottom = scroll_offset + rect.height + + start_idx = 0 + end_idx = len(self._items) + + # Find first visible item + for i, item in enumerate(self._items): + if item.rect and item.rect.y + item.rect.height >= visible_top: + start_idx = max(0, i - 1) + break + + # Find last visible item + for i in range(start_idx, len(self._items)): + item = self._items[i] + if item.rect and item.rect.y > visible_bottom: + end_idx = min(len(self._items), i + 2) + break + + self._visible_range = (start_idx, end_idx) + + def _handle_mouse_interaction(self, rect: rl.Rectangle, scroll_offset: rl.Vector2): + mouse_pos = rl.get_mouse_position() + + self._hovered_item = -1 + if not rl.check_collision_point_rec(mouse_pos, rect): + return + + content_mouse_y = mouse_pos.y - rect.y - scroll_offset.y + + for i, item in enumerate(self._items): + if item.rect: + # Check if mouse is within this item's bounds in content space + if ( + mouse_pos.x >= rect.x + and mouse_pos.x <= rect.x + rect.width + and content_mouse_y >= item.rect.y + and content_mouse_y <= item.rect.y + item.rect.height + ): + item_screen_y = item.rect.y + scroll_offset.y + if item_screen_y < rect.height and item_screen_y + item.rect.height > 0: + self._hovered_item = i + break + + # Handle click on main item (not right item) + if rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT) and self._hovered_item >= 0: + item = self._items[self._hovered_item] + + # Check if click was on right item area + if item.right_item and item.rect: + adjusted_rect = rl.Rectangle(item.rect.x, item.rect.y + scroll_offset.y, item.rect.width, item.rect.height) + right_rect = item.get_right_item_rect(adjusted_rect) + if rl.check_collision_point_rec(mouse_pos, right_rect): + # Click was handled by right item, don't process main item click + return + + # Toggle description visibility if item has description + if item.description: + item.description_visible = not item.description_visible + # Force layout update when description visibility changes + self._last_dim = (0, 0) + + # Call item callback + if item.callback: + item.callback() + + +# Factory functions +def simple_item(title: str, callback: Callable | None = None) -> ListItem: + return ListItem(title=title, callback=callback) + + +def toggle_item( + title: str, description: str = None, initial_state: bool = False, callback: Callable | None = None, icon: str = "" +) -> ListItem: + toggle = ToggleRightItem(initial_state=initial_state) + return ListItem(title=title, description=description, right_item=toggle, icon=icon, callback=callback) + + +def button_item(title: str, button_text: str, description: str = None, callback: Callable | None = None) -> ListItem: + button = ButtonRightItem(text=button_text) + return ListItem(title=title, description=description, right_item=button, callback=callback) + + +def text_item(title: str, value: str, description: str = None, callback: Callable | None = None) -> ListItem: + text_item = TextRightItem(text=value, color=rl.Color(170, 170, 170, 255)) + return ListItem(title=title, description=description, right_item=text_item, callback=callback) diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index 5ae2500406..0128dca206 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -9,9 +9,9 @@ ANIMATION_SPEED = 8.0 class Toggle: - def __init__(self, x, y, initial_state=False): + def __init__(self, initial_state=False): self._state = initial_state - self._rect = rl.Rectangle(x, y, WIDTH, HEIGHT) + self._rect = rl.Rectangle(0, 0, WIDTH, HEIGHT) self._progress = 1.0 if initial_state else 0.0 self._target = self._progress @@ -20,17 +20,23 @@ class Toggle: if rl.check_collision_point_rec(rl.get_mouse_position(), self._rect): self._state = not self._state self._target = 1.0 if self._state else 0.0 + return 1 + return 0 def get_state(self): return self._state + def set_state(self, state: bool): + self._state = state + def update(self): if abs(self._progress - self._target) > 0.01: delta = rl.get_frame_time() * ANIMATION_SPEED self._progress += delta if self._progress < self._target else -delta self._progress = max(0.0, min(1.0, self._progress)) - def render(self): + def render(self, rect: rl.Rectangle): + self._rect.x, self._rect.y = rect.x, rect.y self. update() # Draw background bg_rect = rl.Rectangle(self._rect.x + 5, self._rect.y + 10, WIDTH - 10, BG_HEIGHT) @@ -42,15 +48,7 @@ class Toggle: knob_y = self._rect.y + HEIGHT / 2 rl.draw_circle(int(knob_x), int(knob_y), HEIGHT / 2, KNOB_COLOR) + return self.handle_input() + def _blend_color(self, c1, c2, t): return rl.Color(int(c1.r + (c2.r - c1.r) * t), int(c1.g + (c2.g - c1.g) * t), int(c1.b + (c2.b - c1.b) * t), 255) - - -if __name__ == "__main__": - from openpilot.system.ui.lib.application import gui_app - - gui_app.init_window("Text toggle example") - toggle = Toggle(100, 100) - for _ in gui_app.render(): - toggle.handle_input() - toggle.render() diff --git a/system/ui/lib/wrap_text.py b/system/ui/lib/wrap_text.py new file mode 100644 index 0000000000..fdfb1970aa --- /dev/null +++ b/system/ui/lib/wrap_text.py @@ -0,0 +1,87 @@ +import pyray as rl +from openpilot.system.ui.lib.text_measure import measure_text_cached + +def _break_long_word(font: rl.Font, word: str, font_size: int, max_width: int) -> list[str]: + if not word: + return [] + + parts = [] + remaining = word + + while remaining: + if measure_text_cached(font, remaining, font_size).x <= max_width: + parts.append(remaining) + break + + # Binary search for the longest substring that fits + left, right = 1, len(remaining) + best_fit = 1 + + while left <= right: + mid = (left + right) // 2 + substring = remaining[:mid] + width = measure_text_cached(font, substring, font_size).x + + if width <= max_width: + best_fit = mid + left = mid + 1 + else: + right = mid - 1 + + # Add the part that fits + parts.append(remaining[:best_fit]) + remaining = remaining[best_fit:] + + return parts + + +def wrap_text(font: rl.Font, text: str, font_size: int, max_width: int) -> list[str]: + if not text or max_width <= 0: + return [] + + words = text.split() + if not words: + return [] + + lines: list[str] = [] + current_line: list[str] = [] + current_width = 0 + space_width = int(measure_text_cached(font, " ", font_size).x) + + for word in words: + word_width = int(measure_text_cached(font, word, font_size).x) + + # Check if word alone exceeds max width (need to break the word) + if word_width > max_width: + # Finish current line if it has content + if current_line: + lines.append(" ".join(current_line)) + current_line = [] + current_width = 0 + + # Break the long word into parts + lines.extend(_break_long_word(font, word, font_size, max_width)) + continue + + # Calculate width if we add this word + needed_width = current_width + if current_line: # Need space before word + needed_width += space_width + needed_width += word_width + + # Check if word fits on current line + if needed_width <= max_width: + current_line.append(word) + current_width = needed_width + else: + # Start new line with this word + if current_line: + lines.append(" ".join(current_line)) + current_line = [word] + current_width = word_width + + # Add remaining words + if current_line: + lines.append(" ".join(current_line)) + + return lines From 0360926962ead2b08158b2d4a975ae688ab1190a Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 5 Jun 2025 05:08:29 +0800 Subject: [PATCH 439/477] ui: fill the bg with disengaged color if no frame (#35451) fill the bg with disengaged color if no frame Co-authored-by: Shane Smiskol --- selfdrive/ui/onroad/augmented_road_view.py | 1 + selfdrive/ui/onroad/cameraview.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index fa7469d2dd..d727337e31 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -28,6 +28,7 @@ BORDER_COLORS = { class AugmentedRoadView(CameraView): def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD): super().__init__("camerad", stream_type) + self._set_placeholder_color(BORDER_COLORS[UIStatus.DISENGAGED]) self.device_camera: DeviceCameraConfig | None = None self.view_from_calib = view_frame_from_device_frame.copy() diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index d59e7b0a48..2bc49fec03 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -74,6 +74,8 @@ class CameraView: self.egl_images: dict[int, EGLImage] = {} self.egl_texture: rl.Texture | None = None + self._placeholder_color : rl.Color | None = None + # Initialize EGL for zero-copy rendering on TICI if TICI: if not init_egl(): @@ -84,6 +86,10 @@ class CameraView: self.egl_texture = rl.load_texture_from_image(temp_image) rl.unload_image(temp_image) + def _set_placeholder_color(self, color: rl.Color): + """Set a placeholder color to be drawn when no frame is available.""" + self._placeholder_color = color + def switch_stream(self, stream_type: VisionStreamType) -> None: if self._stream_type != stream_type: cloudlog.debug(f'switching stream from {self._stream_type} to {stream_type}') @@ -130,6 +136,7 @@ class CameraView: def render(self, rect: rl.Rectangle): if not self._ensure_connection(): + self._draw_placeholder(rect) return # Try to get a new buffer without blocking @@ -139,6 +146,7 @@ class CameraView: self.frame = buffer if not self.frame: + self._draw_placeholder(rect) return transform = self._calc_frame_matrix(rect) @@ -163,6 +171,10 @@ class CameraView: else: self._render_textures(src_rect, dst_rect) + def _draw_placeholder(self, rect: rl.Rectangle): + if self._placeholder_color: + rl.draw_rectangle_rec(rect, self._placeholder_color) + def _render_egl(self, src_rect: rl.Rectangle, dst_rect: rl.Rectangle) -> None: """Render using EGL for direct buffer access""" if self.frame is None or self.egl_texture is None: From 8b516abb31ae6c68c52e09771c5e1fb6af05b6b5 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 5 Jun 2025 05:14:40 +0800 Subject: [PATCH 440/477] ui: replace rl.measure_text with measure_text_cached in sidebar (#35450) Replace rl.measure_text with measure_text_cached --- selfdrive/ui/layouts/sidebar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/layouts/sidebar.py b/selfdrive/ui/layouts/sidebar.py index eeb073e67b..343efe5851 100644 --- a/selfdrive/ui/layouts/sidebar.py +++ b/selfdrive/ui/layouts/sidebar.py @@ -5,6 +5,7 @@ from collections.abc import Callable from cereal import log from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.text_measure import measure_text_cached SIDEBAR_WIDTH = 300 METRIC_HEIGHT = 126 @@ -199,7 +200,7 @@ class Sidebar: # Draw text text = f"{metric.label}\n{metric.value}" - text_size = rl.measure_text_ex(self._font_bold, text, 35, 0) + text_size = measure_text_cached(self._font_bold, text, 35) text_pos = rl.Vector2( metric_rect.x + 22 + (metric_rect.width - 22 - text_size.x) / 2, metric_rect.y + (metric_rect.height - text_size.y) / 2 From 462301d2e0f6697ca2c5ca2e126ba387e83fb960 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 5 Jun 2025 05:20:52 +0800 Subject: [PATCH 441/477] ui: implement smooth camera stream switching with seamless transitions (#35449) implement smooth camera stream switching with seamless transitions Co-authored-by: Shane Smiskol --- selfdrive/ui/onroad/augmented_road_view.py | 14 ++-- selfdrive/ui/onroad/cameraview.py | 81 +++++++++++++++++++--- 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index d727337e31..47769f268e 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -16,6 +16,8 @@ from openpilot.common.transformations.orientation import rot_from_euler OpState = log.SelfdriveState.OpenpilotState CALIBRATED = log.LiveCalibrationData.Status.calibrated +ROAD_CAM = VisionStreamType.VISION_STREAM_ROAD +WIDE_CAM = VisionStreamType.VISION_STREAM_WIDE_ROAD DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"] BORDER_COLORS = { @@ -36,6 +38,7 @@ class AugmentedRoadView(CameraView): self._last_calib_time: float = 0 self._last_rect_dims = (0.0, 0.0) + self._last_stream_type = stream_type self._cached_matrix: np.ndarray | None = None self._content_rect = rl.Rectangle() @@ -120,6 +123,7 @@ class AugmentedRoadView(CameraView): current_dims = (self._content_rect.width, self._content_rect.height) if (self._last_calib_time == calib_time and self._last_rect_dims == current_dims and + self._last_stream_type == self.stream_type and self._cached_matrix is not None): return self._cached_matrix @@ -155,9 +159,10 @@ class AugmentedRoadView(CameraView): except (ZeroDivisionError, OverflowError): x_offset, y_offset = 0, 0 - # Update cache values + # Cache the computed transformation matrix to avoid recalculations self._last_calib_time = calib_time self._last_rect_dims = current_dims + self._last_stream_type = self.stream_type self._cached_matrix = np.array([ [zoom * 2 * cx / w, 0, -x_offset / w * 2], [0, zoom * 2 * cy / h, -y_offset / h * 2], @@ -176,14 +181,15 @@ class AugmentedRoadView(CameraView): if __name__ == "__main__": gui_app.init_window("OnRoad Camera View") - road_camera_view = AugmentedRoadView(VisionStreamType.VISION_STREAM_ROAD) + road_camera_view = AugmentedRoadView(ROAD_CAM) print("***press space to switch camera view***") try: for _ in gui_app.render(): ui_state.update() if rl.is_key_released(rl.KeyboardKey.KEY_SPACE): - is_wide = road_camera_view.stream_type == VisionStreamType.VISION_STREAM_WIDE_ROAD - road_camera_view.switch_stream(VisionStreamType.VISION_STREAM_ROAD if is_wide else VisionStreamType.VISION_STREAM_WIDE_ROAD) + if WIDE_CAM in road_camera_view.available_streams: + stream = ROAD_CAM if road_camera_view.stream_type == WIDE_CAM else WIDE_CAM + road_camera_view.switch_stream(stream) road_camera_view.render(rl.Rectangle(0, 0, gui_app.width, gui_app.height)) finally: road_camera_view.close() diff --git a/selfdrive/ui/onroad/cameraview.py b/selfdrive/ui/onroad/cameraview.py index 2bc49fec03..9f58133abf 100644 --- a/selfdrive/ui/onroad/cameraview.py +++ b/selfdrive/ui/onroad/cameraview.py @@ -57,9 +57,17 @@ else: class CameraView: def __init__(self, name: str, stream_type: VisionStreamType): - self.client = VisionIpcClient(name, stream_type, conflate=True) self._name = name + # Primary stream + self.client = VisionIpcClient(name, stream_type, conflate=True) self._stream_type = stream_type + self.available_streams: list[VisionStreamType] = [] + + # Target stream for switching + self._target_client: VisionIpcClient | None = None + self._target_stream_type: VisionStreamType | None = None + self._switching: bool = False + self._texture_needs_update = True self.last_connection_attempt: float = 0.0 @@ -91,12 +99,20 @@ class CameraView: self._placeholder_color = color def switch_stream(self, stream_type: VisionStreamType) -> None: - if self._stream_type != stream_type: - cloudlog.debug(f'switching stream from {self._stream_type} to {stream_type}') - self._clear_textures() - self.frame = None - self._stream_type = stream_type - self.client = VisionIpcClient(self._name, stream_type, conflate=True) + if self._stream_type == stream_type: + return + + if self._switching and self._target_stream_type == stream_type: + return + + cloudlog.debug(f'Preparing switch from {self._stream_type} to {stream_type}') + + if self._target_client: + del self._target_client + + self._target_stream_type = stream_type + self._target_client = VisionIpcClient(self._name, stream_type, conflate=True) + self._switching = True @property def stream_type(self) -> VisionStreamType: @@ -135,6 +151,9 @@ class CameraView: ]) def render(self, rect: rl.Rectangle): + if self._switching: + self._handle_switch() + if not self._ensure_connection(): self._draw_placeholder(rect) return @@ -226,6 +245,7 @@ class CameraView: def _ensure_connection(self) -> bool: if not self.client.is_connected(): self.frame = None + self.available_streams.clear() # Throttle connection attempts current_time = rl.get_time() @@ -237,16 +257,57 @@ class CameraView: return False cloudlog.debug(f"Connected to {self._name} stream: {self._stream_type}, buffers: {self.client.num_buffers}") - self._clear_textures() + self._initialize_textures() + self.available_streams = self.client.available_streams(self._name, block=False) + return True + + def _handle_switch(self) -> None: + """Check if target stream is ready and switch immediately.""" + if not self._target_client or not self._switching: + return + + # Try to connect target if needed + if not self._target_client.is_connected(): + if not self._target_client.connect(False) or not self._target_client.num_buffers: + return + + cloudlog.debug(f"Target stream connected: {self._target_stream_type}") + + # Check if target has frames ready + target_frame = self._target_client.recv(timeout_ms=0) + if target_frame: + self.frame = target_frame # Update current frame to target frame + self._complete_switch() + + def _complete_switch(self) -> None: + """Instantly switch to target stream.""" + cloudlog.debug(f"Switching to {self._target_stream_type}") + # Clean up current resources + if self.client: + del self.client + + # Switch to target + self.client = self._target_client + self._stream_type = self._target_stream_type + self._texture_needs_update = True + + # Reset state + self._target_client = None + self._target_stream_type = None + self._switching = False + + # Initialize textures for new stream + self._initialize_textures() + + def _initialize_textures(self): + self._clear_textures() if not TICI: self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride), int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)) self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), int(self.client.height // 2), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)) - return True - def _clear_textures(self): if self.texture_y and self.texture_y.id: rl.unload_texture(self.texture_y) From 0d7b157965e246762f93a69456be46a7f78c803c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 4 Jun 2025 15:10:52 -0700 Subject: [PATCH 442/477] bump opendbc (#35465) bump --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 0d7fef4422..ed0fe857f9 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 0d7fef4422456aeb456fa895da1617a1f61f6896 +Subproject commit ed0fe857f9cda9720250eef9139210d58062cd4c From 57dc9152bb48b0a2bd158fa90d585d0263cea27f Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Wed, 4 Jun 2025 21:10:26 -0700 Subject: [PATCH 443/477] Networking: Fix Prime Level Five For Users in Roaming Only Networks (#986) Fix prime level five for users in roaming only networks. --- selfdrive/ui/qt/network/networking.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 41527fe692..7754e6e5ba 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -79,7 +79,8 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) { } void Networking::setPrimeType(PrimeState::Type type) { - an->setGsmVisible(type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_LITE); + an->setGsmVisible(type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_UNKNOWN || \ + type == PrimeState::PRIME_TYPE_PURPLE || type == PrimeState::PRIME_TYPE_LITE); wifi->ipv4_forward = (type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_LITE); } From 0c506c868d43251f82bf33580f5ba44078d7a4dc Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 5 Jun 2025 00:21:58 -0400 Subject: [PATCH 444/477] Controls: Pause lateral control based on blinker state and vehicle speed (#983) * Controls: Pause lateral control based on blinker state and vehicle speed * in its own module * tests * cleanup tests * ui * always refresh on show panel * remove default * change to 20 default * need to update params live * shorter * off by default * lol lint * use ExpandableToggleRow * not needed * shorter * ci ui preview delay for all offroad * more * Revert "more" This reverts commit 809cfd99dac4183e318f3f7dff8e7c14f8223d62. * Revert "ci ui preview delay for all offroad" This reverts commit ab38292fa84165081e32fcfc7b44f7f7afdfe791. --------- Co-authored-by: nayan8teen --- common/params_keys.h | 2 + selfdrive/ui/sunnypilot/SConscript | 1 + .../lateral/blinker_pause_lateral_settings.cc | 27 ++++ .../lateral/blinker_pause_lateral_settings.h | 25 ++++ .../qt/offroad/settings/lateral_panel.cc | 13 ++ .../qt/offroad/settings/lateral_panel.h | 2 + .../selfdrive/controls/controlsd_ext.py | 10 +- .../controls/lib/blinker_pause_lateral.py | 34 +++++ .../lib/tests/test_blinker_pause_lateral.py | 130 ++++++++++++++++++ system/manager/manager.py | 2 + 10 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h create mode 100644 sunnypilot/selfdrive/controls/lib/blinker_pause_lateral.py create mode 100644 sunnypilot/selfdrive/controls/lib/tests/test_blinker_pause_lateral.py diff --git a/common/params_keys.h b/common/params_keys.h index a28a02693b..6474fb5840 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -125,6 +125,8 @@ inline static std::unordered_map keys = { {"ApiCache_DriveStats", PERSISTENT}, {"AutoLaneChangeBsmDelay", PERSISTENT}, {"AutoLaneChangeTimer", PERSISTENT}, + {"BlinkerMinLateralControlSpeed", PERSISTENT | BACKUP}, + {"BlinkerPauseLateralControl", PERSISTENT | BACKUP}, {"CarParamsSP", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"CarParamsSPCache", CLEAR_ON_MANAGER_START}, {"CarParamsSPPersistent", PERSISTENT}, diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 0353c9babd..796107da77 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -40,6 +40,7 @@ qt_src = [ ] lateral_panel_qt_src = [ + "sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.cc", "sunnypilot/qt/offroad/settings/lateral/lane_change_settings.cc", "sunnypilot/qt/offroad/settings/lateral/mads_settings.cc", "sunnypilot/qt/offroad/settings/lateral/neural_network_lateral_control.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.cc new file mode 100644 index 0000000000..b43c6162c6 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.cc @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h" + +BlinkerPauseLateralSettings::BlinkerPauseLateralSettings(const QString ¶m, const QString &title, const QString &description, const QString &icon, QWidget *parent) + : ExpandableToggleRow(param, title, description, icon, parent) { + + pauseLateralSpeed = new OptionControlSP("BlinkerMinLateralControlSpeed", "", "", "", {0, 255}, 5); + connect(pauseLateralSpeed, &OptionControlSP::updateLabels, this, &BlinkerPauseLateralSettings::refresh); + addItem(pauseLateralSpeed); + + refresh(); +} + +void BlinkerPauseLateralSettings::refresh() { + const QString option = QString::fromStdString(params.get("BlinkerMinLateralControlSpeed")); + const bool is_metric = params.getBool("IsMetric"); + const QString unit = is_metric ? "km/h" : "mph"; + + pauseLateralSpeed->setLabel(option + " " + unit); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h new file mode 100644 index 0000000000..7f02337b9c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h" + +class BlinkerPauseLateralSettings : public ExpandableToggleRow { + Q_OBJECT + +public: + BlinkerPauseLateralSettings(const QString ¶m, const QString &title, const QString &description, const QString &icon, QWidget *parent = nullptr); + void refresh(); + +private: + Params params; + OptionControlSP *pauseLateralSpeed; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc index 0beae6e24f..ae954d7539 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc @@ -64,6 +64,17 @@ LateralPanel::LateralPanel(SettingsWindowSP *parent) : QFrame(parent) { list->addItem(vertical_space(0)); list->addItem(horizontal_line()); + // Blinker Pause Lateral Control + blinkerPauseLateralSettings = new BlinkerPauseLateralSettings( + "BlinkerPauseLateralControl", + tr("Pause Lateral Control with Blinker"), + tr("Pause lateral control with blinker when traveling below the desired speed selected."), + "", + this); + list->addItem(blinkerPauseLateralSettings); + + list->addItem(horizontal_line()); + // Neural Network Lateral Control nnlcToggle = new NeuralNetworkLateralControl(); list->addItem(nnlcToggle); @@ -139,5 +150,7 @@ void LateralPanel::updateToggles(bool _offroad) { madsSettingsButton->setEnabled(madsToggle->isToggled()); + blinkerPauseLateralSettings->refresh(); + offroad = _offroad; } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h index cf003dfaf3..7b5e73fceb 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h @@ -11,6 +11,7 @@ #include #include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/neural_network_lateral_control.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/lane_change_settings.h" @@ -43,6 +44,7 @@ private: PushButtonSP *laneChangeSettingsButton; LaneChangeSettings *laneChangeWidget = nullptr; NeuralNetworkLateralControl *nnlcToggle = nullptr; + BlinkerPauseLateralSettings *blinkerPauseLateralSettings = nullptr; const QString MADS_BASE_DESC = tr("Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC)."); diff --git a/sunnypilot/selfdrive/controls/controlsd_ext.py b/sunnypilot/selfdrive/controls/controlsd_ext.py index 4978c01332..9a7f0cb5fc 100644 --- a/sunnypilot/selfdrive/controls/controlsd_ext.py +++ b/sunnypilot/selfdrive/controls/controlsd_ext.py @@ -11,12 +11,14 @@ from opendbc.car import structs from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.sunnypilot.selfdrive.controls.lib.param_store import ParamStore +from openpilot.sunnypilot.selfdrive.controls.lib.blinker_pause_lateral import BlinkerPauseLateral class ControlsExt: def __init__(self, CP: structs.CarParams, params: Params): self.CP = CP self.params = params + self.blinker_pause_lateral = BlinkerPauseLateral() self.param_store = ParamStore(self.CP) self.get_params_sp() @@ -29,11 +31,13 @@ class ControlsExt: def get_params_sp(self) -> None: self.param_store.update(self.params) + self.blinker_pause_lateral.get_params() + + def get_lat_active(self, sm: messaging.SubMaster) -> bool: + if self.blinker_pause_lateral.update(sm['carState']): + return False - @staticmethod - def get_lat_active(sm: messaging.SubMaster) -> bool: ss_sp = sm['selfdriveStateSP'] - if ss_sp.mads.available: return bool(ss_sp.mads.active) diff --git a/sunnypilot/selfdrive/controls/lib/blinker_pause_lateral.py b/sunnypilot/selfdrive/controls/lib/blinker_pause_lateral.py new file mode 100644 index 0000000000..fb86280358 --- /dev/null +++ b/sunnypilot/selfdrive/controls/lib/blinker_pause_lateral.py @@ -0,0 +1,34 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from cereal import car + +from openpilot.common.conversions import Conversions as CV +from openpilot.common.params import Params + + +class BlinkerPauseLateral: + def __init__(self): + self.params = Params() + + self.enabled = self.params.get_bool("BlinkerPauseLateralControl") + self.is_metric = self.params.get_bool("IsMetric") + self.min_speed = 0 + + def get_params(self) -> None: + self.enabled = self.params.get_bool("BlinkerPauseLateralControl") + self.is_metric = self.params.get_bool("IsMetric") + self.min_speed = int(self.params.get("BlinkerMinLateralControlSpeed", encoding='utf8')) + + def update(self, CS: car.CarState) -> bool: + if not self.enabled: + return False + + one_blinker = CS.leftBlinker != CS.rightBlinker + speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS + min_speed_ms = self.min_speed * speed_factor + + return bool(one_blinker and CS.vEgo < min_speed_ms) diff --git a/sunnypilot/selfdrive/controls/lib/tests/test_blinker_pause_lateral.py b/sunnypilot/selfdrive/controls/lib/tests/test_blinker_pause_lateral.py new file mode 100644 index 0000000000..6ba1dcef41 --- /dev/null +++ b/sunnypilot/selfdrive/controls/lib/tests/test_blinker_pause_lateral.py @@ -0,0 +1,130 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from cereal import car + +from openpilot.common.conversions import Conversions as CV +from openpilot.sunnypilot.selfdrive.controls.lib.blinker_pause_lateral import BlinkerPauseLateral + + +class TestBlinkerPauseLateral: + + def setup_method(self): + self.blinker_pause_lateral = BlinkerPauseLateral() + self._reset_states() + + def _reset_states(self): + self.blinker_pause_lateral.enabled = True + self.blinker_pause_lateral.is_metric = False + self.blinker_pause_lateral.min_speed = 20 # MPH + + self.CS = car.CarState.new_message() + self.CS.vEgo = 0 + self.CS.leftBlinker = False + self.CS.rightBlinker = False + + def _test_should_blinker_pause_lateral(self, expected_results) -> None: + for left in (True, False): + for right in (True, False): + self.CS.leftBlinker = left + self.CS.rightBlinker = right + + result = self.blinker_pause_lateral.update(self.CS) + assert result == expected_results[(left, right)] + + def test_below_min_speed_blinker(self): + self.CS.vEgo = 4.5 # ~10 MPH + + expected_results = { + (False, False): False, + (True, False): True, + (False, True): True, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + def test_above_min_speed_blinker(self): + self.CS.vEgo = 13.4 # ~30 MPH + + expected_results = { + (False, False): False, + (True, False): False, + (False, True): False, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + def test_just_below_min_speed(self): + self.CS.vEgo = (20 * CV.MPH_TO_MS) - 0.01 + + expected_results = { + (False, False): False, + (True, False): True, + (False, True): True, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + def test_disabled(self): + self.blinker_pause_lateral.enabled = False + self.CS.vEgo = 4.5 # ~10 MPH + + expected_results = { + (False, False): False, + (True, False): False, + (False, True): False, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + def test_metric_units_below_min_speed(self): + self.blinker_pause_lateral.is_metric = True + self.CS.vEgo = 5.0 # ~18 km/h + + expected_results = { + (False, False): False, + (True, False): True, + (False, True): True, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + def test_metric_units_above_threshold(self): + self.blinker_pause_lateral.is_metric = True + self.CS.vEgo = 6.0 # ~21.6 km/h + + expected_results = { + (False, False): False, + (True, False): False, + (False, True): False, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + def test_change_min_speed_threshold(self): + self.blinker_pause_lateral.min_speed = 30 # MPH + + # below min speed + self.CS.vEgo = 11.2 # ~25 MPH + + expected_results = { + (False, False): False, + (True, False): True, + (False, True): True, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) + + # above min speed + self.CS.vEgo = 15.6 # ~35 MPH + + expected_results = { + (False, False): False, + (True, False): False, + (False, True): False, + (True, True): False + } + self._test_should_blinker_pause_lateral(expected_results) diff --git a/system/manager/manager.py b/system/manager/manager.py index 71f2bc525a..46b849108c 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -44,6 +44,8 @@ def manager_init() -> None: sunnypilot_default_params: list[tuple[str, str | bytes]] = [ ("AutoLaneChangeTimer", "0"), ("AutoLaneChangeBsmDelay", "0"), + ("BlinkerMinLateralControlSpeed", "20"), # MPH or km/h + ("BlinkerPauseLateralControl", "0"), ("DynamicExperimentalControl", "0"), ("HyundaiLongitudinalTuning", "0"), ("Mads", "1"), From a8639d3feb79f77278f87f4517778b38eeea3fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Sch=C3=A4fer?= Date: Wed, 4 Jun 2025 23:40:41 -0700 Subject: [PATCH 445/477] rgb is default (#35467) * rgb is default * change dummy default --- selfdrive/test/process_replay/regen.py | 2 +- tools/lib/framereader.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/test/process_replay/regen.py b/selfdrive/test/process_replay/regen.py index 4d00613f0a..273659c9ff 100755 --- a/selfdrive/test/process_replay/regen.py +++ b/selfdrive/test/process_replay/regen.py @@ -23,7 +23,7 @@ class DummyFrameReader(BaseFrameReader): self.frame_count = frame_count self.frame_type = FrameType.raw - def get(self, idx, count=1, pix_fmt="yuv420p"): + def get(self, idx, count=1, pix_fmt="rgb24"): if pix_fmt == "rgb24": shape = (self.h, self.w, 3) elif pix_fmt == "nv12" or pix_fmt == "yuv420p": diff --git a/tools/lib/framereader.py b/tools/lib/framereader.py index 275b9b65b8..0eddc88868 100644 --- a/tools/lib/framereader.py +++ b/tools/lib/framereader.py @@ -209,7 +209,7 @@ class BaseFrameReader: def close(self): pass - def get(self, num, count=1, pix_fmt="yuv420p"): + def get(self, num, count=1, pix_fmt="rgb24"): raise NotImplementedError @@ -497,7 +497,7 @@ class GOPFrameReader(BaseFrameReader): return self.frame_cache[(num, pix_fmt)] - def get(self, num, count=1, pix_fmt="yuv420p"): + def get(self, num, count=1, pix_fmt="rgb24"): assert self.frame_count is not None if num + count > self.frame_count: @@ -523,12 +523,12 @@ class StreamFrameReader(StreamGOPReader, GOPFrameReader): GOPFrameReader.__init__(self, readahead, readbehind) -def GOPFrameIterator(gop_reader, pix_fmt): +def GOPFrameIterator(gop_reader, pix_fmt='rgb24'): dec = VideoStreamDecompressor(gop_reader.fn, gop_reader.vid_fmt, gop_reader.w, gop_reader.h, pix_fmt) yield from dec.read() -def FrameIterator(fn, pix_fmt, **kwargs): +def FrameIterator(fn, pix_fmt='rgb24', **kwargs): fr = FrameReader(fn, **kwargs) if isinstance(fr, GOPReader): yield from GOPFrameIterator(fr, pix_fmt) From 05fa135be662fd8d8772b5f8cb15818c67cf744d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 6 Jun 2025 01:13:44 +0800 Subject: [PATCH 446/477] ui: make the toggle color match QT (#35466) make the toggle color match QT --- system/ui/lib/toggle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index 0128dca206..32b9ca5416 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -1,6 +1,6 @@ import pyray as rl -ON_COLOR = rl.Color(0, 255, 0, 255) +ON_COLOR = rl.Color(51, 171, 76, 255) OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) KNOB_COLOR = rl.WHITE WIDTH, HEIGHT = 160, 80 From 3ad74b440c29e72d139ca097de097ff199487a5b Mon Sep 17 00:00:00 2001 From: Andrei Radulescu Date: Thu, 5 Jun 2025 20:23:27 +0300 Subject: [PATCH 447/477] macos: no logcatd and proclogd (#35457) * don't build proclogd on mac * don't run logcatd and proclogd on mac --- SConstruct | 2 +- system/manager/process_config.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index c534eedbe8..d5051c8fb2 100644 --- a/SConstruct +++ b/SConstruct @@ -342,13 +342,13 @@ SConscript(['rednose/SConscript']) # Build system services SConscript([ - 'system/proclogd/SConscript', 'system/ubloxd/SConscript', 'system/loggerd/SConscript', ]) if arch != "Darwin": SConscript([ 'system/logcatd/SConscript', + 'system/proclogd/SConscript', ]) if arch == "larch64": diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 738c20a85b..ecd5b724cd 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -1,5 +1,6 @@ import os import operator +import platform from cereal import car from openpilot.common.params import Params @@ -70,8 +71,8 @@ procs = [ NativeProcess("camerad", "system/camerad", ["./camerad"], driverview, enabled=not WEBCAM), PythonProcess("webcamerad", "tools.webcam.camerad", driverview, enabled=WEBCAM), - NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad), - NativeProcess("proclogd", "system/proclogd", ["./proclogd"], only_onroad), + NativeProcess("logcatd", "system/logcatd", ["./logcatd"], only_onroad, platform.system() != "Darwin"), + NativeProcess("proclogd", "system/proclogd", ["./proclogd"], only_onroad, platform.system() != "Darwin"), PythonProcess("micd", "system.micd", iscar), PythonProcess("timed", "system.timed", always_run, enabled=not PC), From ef6719f24eeef7c93118e85477b7cb213aea21cc Mon Sep 17 00:00:00 2001 From: Andrei Radulescu Date: Thu, 5 Jun 2025 20:23:51 +0300 Subject: [PATCH 448/477] webcam: make driver camera optional (#35455) * webcam: driverCamera optional * webcam: update README --- tools/webcam/README.md | 20 ++++++++++---------- tools/webcam/camerad.py | 9 +++++++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tools/webcam/README.md b/tools/webcam/README.md index f4b46d1f66..67ad2cc8cb 100644 --- a/tools/webcam/README.md +++ b/tools/webcam/README.md @@ -1,22 +1,22 @@ # Run openpilot with webcam on PC What's needed: -- Ubuntu 24.04 ([WSL2 is not supported](https://github.com/commaai/openpilot/issues/34216)) +- Ubuntu 24.04 ([WSL2 is not supported](https://github.com/commaai/openpilot/issues/34216)) or macOS - GPU (recommended) -- Two USB webcams, at least 720p and 78 degrees FOV (e.g. Logitech C920/C615) -- [Car harness](https://comma.ai/shop/products/comma-car-harness) with black panda to connect to your car -- [Panda paw](https://comma.ai/shop/products/panda-paw) or USB-A to USB-A cable to connect panda to your computer -That's it! +- One USB webcam, at least 720p and 78 degrees FOV (e.g. Logitech C920/C615, NexiGo N60) +- [Car harness](https://comma.ai/shop/products/comma-car-harness) +- [panda](https://comma.ai/shop/panda) +- USB-A to USB-A cable to connect panda to your computer ## Setup openpilot - Follow [this readme](../README.md) to install and build the requirements -- Install OpenCL Driver +- Install OpenCL Driver (Ubuntu) ``` sudo apt install pocl-opencl-icd ``` ## Connect the hardware -- Connect the road facing camera first, then the driver facing camera +- Connect the camera first - Connect your computer to panda ## GO @@ -24,12 +24,12 @@ sudo apt install pocl-opencl-icd USE_WEBCAM=1 system/manager/manager.py ``` - Start the car, then the UI should show the road webcam's view -- Adjust and secure the webcams. +- Adjust and secure the webcam - Finish calibration and engage! ## Specify Cameras -Use the `ROAD_CAM`, `DRIVER_CAM`, and optional `WIDE_CAM` environment variables to specify which camera is which (ie. `DRIVER_CAM=2` uses `/dev/video2` for the driver-facing camera): +Use the `ROAD_CAM` (default 0) and optional `DRIVER_CAM`, `WIDE_CAM` environment variables to specify which camera is which (ie. `ROAD_CAM=1` uses `/dev/video1`, on Ubuntu, for the road camera): ``` -USE_WEBCAM=1 ROAD_CAM=4 WIDE_CAM=6 system/manager/manager.py +USE_WEBCAM=1 ROAD_CAM=1 system/manager/manager.py ``` diff --git a/tools/webcam/camerad.py b/tools/webcam/camerad.py index a0916ed5ee..cb096dc00b 100755 --- a/tools/webcam/camerad.py +++ b/tools/webcam/camerad.py @@ -9,14 +9,19 @@ from cereal import messaging from openpilot.tools.webcam.camera import Camera from openpilot.common.realtime import Ratekeeper +ROAD_CAM = os.getenv("ROAD_CAM", "0") WIDE_CAM = os.getenv("WIDE_CAM") +DRIVER_CAM = os.getenv("DRIVER_CAM") + CameraType = namedtuple("CameraType", ["msg_name", "stream_type", "cam_id"]) + CAMERAS = [ - CameraType("roadCameraState", VisionStreamType.VISION_STREAM_ROAD, os.getenv("ROAD_CAM", "0")), - CameraType("driverCameraState", VisionStreamType.VISION_STREAM_DRIVER, os.getenv("DRIVER_CAM", "2")), + CameraType("roadCameraState", VisionStreamType.VISION_STREAM_ROAD, ROAD_CAM) ] if WIDE_CAM: CAMERAS.append(CameraType("wideRoadCameraState", VisionStreamType.VISION_STREAM_WIDE_ROAD, WIDE_CAM)) +if DRIVER_CAM: + CAMERAS.append(CameraType("driverCameraState", VisionStreamType.VISION_STREAM_DRIVER, DRIVER_CAM)) class Camerad: def __init__(self): From 459179ff6c3e2313897332029a28a3f6a9b1c603 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 6 Jun 2025 02:01:49 +0800 Subject: [PATCH 449/477] ui: fix settings close button shape and press state (#35469) fix button rounding and pressed state display in settings --- selfdrive/ui/layouts/settings/settings.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index fc03529e56..849a26f0dd 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -50,8 +50,6 @@ class SettingsLayout: def __init__(self): self._params = Params() self._current_panel = PanelType.DEVICE - self._close_btn_pressed = False - self._scroll_offset = 0.0 self._max_scroll = 0.0 # Panel configuration @@ -93,12 +91,15 @@ class SettingsLayout: rect.x + (rect.width - CLOSE_BTN_SIZE) / 2, rect.y + 45, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE ) - close_color = CLOSE_BTN_PRESSED if self._close_btn_pressed else CLOSE_BTN_COLOR - rl.draw_rectangle_rounded(close_btn_rect, 0.5, 20, close_color) + pressed = (rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and + rl.check_collision_point_rec(rl.get_mouse_position(), close_btn_rect)) + close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR + rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color) + close_text_size = rl.measure_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, 140, 0) close_text_pos = rl.Vector2( close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2, - close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2 - 20, + close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2, ) rl.draw_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, close_text_pos, 140, 0, TEXT_SELECTED) @@ -121,7 +122,6 @@ class SettingsLayout: # Button styling is_selected = panel_type == self._current_panel text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL - # Draw button text (right-aligned) text_size = rl.measure_text_ex(self._font_medium, panel_info.name, 65, 0) text_pos = rl.Vector2( @@ -155,7 +155,6 @@ class SettingsLayout: def handle_mouse_release(self, mouse_pos: rl.Vector2) -> bool: # Check close button if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect): - self._close_btn_pressed = True if self._close_callback: self._close_callback() return True @@ -171,9 +170,6 @@ class SettingsLayout: def _switch_to_panel(self, panel_type: PanelType): if panel_type != self._current_panel: self._current_panel = panel_type - self._scroll_offset = 0.0 # Reset scroll when switching panels - self._transition_progress = 0.0 - self._transitioning = True def set_current_panel(self, index: int, param: str = ""): panel_types = list(self._panels.keys()) From 1668a46b6a74d53dbd81e6beb48b91d0212e2908 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 5 Jun 2025 11:42:05 -0700 Subject: [PATCH 450/477] bump submodules --- opendbc_repo | 2 +- panda | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/opendbc_repo b/opendbc_repo index ed0fe857f9..e370a03607 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit ed0fe857f9cda9720250eef9139210d58062cd4c +Subproject commit e370a036073bf38856f3cd9936c0bd44ff349dea diff --git a/panda b/panda index c19692245c..e462c34d3d 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit c19692245cfe681389a1e9ddb2401340cf08759d +Subproject commit e462c34d3d5402c2c4a274bee7abaafabf516993 From 8aadf02b2fd91f4e1285e18c2c7feb32d93b66f5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 5 Jun 2025 12:51:21 -0700 Subject: [PATCH 451/477] selfdrived: more robust NVMe check --- selfdrive/selfdrived/selfdrived.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index f3dabf3791..8529dff4e2 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -117,7 +117,8 @@ class SelfdriveD: # some comma three with NVMe experience NVMe dropouts mid-drive that # cause loggerd to crash on write, so ignore it only on that platform self.ignored_processes = set() - if HARDWARE.get_device_type() == 'tici' and os.path.exists('/dev/nvme0'): + nvme_expected = os.path.exists('/dev/nvme0n1') or (not os.path.isfile("/persist/comma/living-in-the-moment")) + if HARDWARE.get_device_type() == 'tici' and nvme_expected: self.ignored_processes = {'loggerd', } # Determine startup event From 23ea85dca656e3646354799bf59d896386c09ac0 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 6 Jun 2025 04:14:57 +0800 Subject: [PATCH 452/477] ui: kick watchdog (#35460) * kick watchdog * use global * use monotonic --- common/watchdog.py | 22 ++++++++++++++++++++++ selfdrive/ui/ui.py | 4 ++++ system/manager/process.py | 3 +-- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 common/watchdog.py diff --git a/common/watchdog.py b/common/watchdog.py new file mode 100644 index 0000000000..ddb6f744e9 --- /dev/null +++ b/common/watchdog.py @@ -0,0 +1,22 @@ +import os +import time +import struct +from openpilot.system.hardware.hw import Paths + +WATCHDOG_FN = f"{Paths.shm_path()}/wd_" +_LAST_KICK = 0.0 + +def kick_watchdog(): + global _LAST_KICK + current_time = time.monotonic() + + if current_time - _LAST_KICK < 1.0: + return + + try: + with open(f"{WATCHDOG_FN}{os.getpid()}", 'wb') as f: + f.write(struct.pack(' Date: Fri, 6 Jun 2025 04:15:12 +0800 Subject: [PATCH 453/477] ui: add support for enabling/disabling the toggle (#35471) add support for enabling/disabling the toggle --- system/ui/lib/toggle.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/system/ui/lib/toggle.py b/system/ui/lib/toggle.py index 32b9ca5416..5b3fbf277c 100644 --- a/system/ui/lib/toggle.py +++ b/system/ui/lib/toggle.py @@ -3,6 +3,9 @@ import pyray as rl ON_COLOR = rl.Color(51, 171, 76, 255) OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) KNOB_COLOR = rl.WHITE +DISABLED_ON_COLOR = rl.Color(0x22, 0x77, 0x22, 255) # Dark green when disabled + on +DISABLED_OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255) +DISABLED_KNOB_COLOR = rl.Color(0x88, 0x88, 0x88, 255) WIDTH, HEIGHT = 160, 80 BG_HEIGHT = 60 ANIMATION_SPEED = 8.0 @@ -11,11 +14,15 @@ ANIMATION_SPEED = 8.0 class Toggle: def __init__(self, initial_state=False): self._state = initial_state + self._enabled = True self._rect = rl.Rectangle(0, 0, WIDTH, HEIGHT) self._progress = 1.0 if initial_state else 0.0 self._target = self._progress def handle_input(self): + if not self._enabled: + return 0 + if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON): if rl.check_collision_point_rec(rl.get_mouse_position(), self._rect): self._state = not self._state @@ -28,6 +35,13 @@ class Toggle: def set_state(self, state: bool): self._state = state + self._target = 1.0 if state else 0.0 + + def set_enabled(self, enabled: bool): + self._enabled = enabled + + def is_enabled(self): + return self._enabled def update(self): if abs(self._progress - self._target) > 0.01: @@ -37,16 +51,23 @@ class Toggle: def render(self, rect: rl.Rectangle): self._rect.x, self._rect.y = rect.x, rect.y - self. update() + self.update() + + if self._enabled: + bg_color = self._blend_color(OFF_COLOR, ON_COLOR, self._progress) + knob_color = KNOB_COLOR + else: + bg_color = self._blend_color(DISABLED_OFF_COLOR, DISABLED_ON_COLOR, self._progress) + knob_color = DISABLED_KNOB_COLOR + # Draw background bg_rect = rl.Rectangle(self._rect.x + 5, self._rect.y + 10, WIDTH - 10, BG_HEIGHT) - bg_color = self._blend_color(OFF_COLOR, ON_COLOR, self._progress) rl.draw_rectangle_rounded(bg_rect, 1.0, 10, bg_color) # Draw knob knob_x = self._rect.x + HEIGHT / 2 + (WIDTH - HEIGHT) * self._progress knob_y = self._rect.y + HEIGHT / 2 - rl.draw_circle(int(knob_x), int(knob_y), HEIGHT / 2, KNOB_COLOR) + rl.draw_circle(int(knob_x), int(knob_y), HEIGHT / 2, knob_color) return self.handle_input() From 6b59f67ab548415b98cbd765b20a80cd4b25381d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 6 Jun 2025 04:16:07 +0800 Subject: [PATCH 454/477] ui: implement home layout with fully functional offroad alerts (#35468) implement home layout with offroad alerts --- selfdrive/ui/layouts/home.py | 213 +++++++++++++++- selfdrive/ui/widgets/offroad_alerts.py | 328 +++++++++++++++++++++++++ 2 files changed, 532 insertions(+), 9 deletions(-) create mode 100644 selfdrive/ui/widgets/offroad_alerts.py diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index 56df253b52..55de9e82f1 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -1,17 +1,212 @@ +import time import pyray as rl -from openpilot.system.ui.lib.label import gui_text_box +from collections.abc import Callable +from enum import IntEnum +from openpilot.common.params import Params +from openpilot.selfdrive.ui.widgets.offroad_alerts import UpdateAlert, OffroadAlert +from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.label import gui_label +from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR + + +HEADER_HEIGHT = 80 +HEAD_BUTTON_FONT_SIZE = 40 +CONTENT_MARGIN = 40 +SPACING = 25 +RIGHT_COLUMN_WIDTH = 750 +REFRESH_INTERVAL = 10.0 + +PRIME_BG_COLOR = rl.Color(51, 51, 51, 255) + + +class HomeLayoutState(IntEnum): + HOME = 0 + UPDATE = 1 + ALERTS = 2 class HomeLayout: def __init__(self): - pass + self.params = Params() + + self.update_alert = UpdateAlert() + self.offroad_alert = OffroadAlert() + + self.current_state = HomeLayoutState.HOME + self.last_refresh = 0 + self.settings_callback: callable | None = None + + self.update_available = False + self.alert_count = 0 + + self.header_rect = rl.Rectangle(0, 0, 0, 0) + self.content_rect = rl.Rectangle(0, 0, 0, 0) + self.left_column_rect = rl.Rectangle(0, 0, 0, 0) + self.right_column_rect = rl.Rectangle(0, 0, 0, 0) + + self.update_notif_rect = rl.Rectangle(0, 0, 200, HEADER_HEIGHT - 10) + self.alert_notif_rect = rl.Rectangle(0, 0, 220, HEADER_HEIGHT - 10) + + self._setup_callbacks() + + def _setup_callbacks(self): + self.update_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME)) + self.offroad_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME)) + + def set_settings_callback(self, callback: Callable): + self.settings_callback = callback + + def _set_state(self, state: HomeLayoutState): + self.current_state = state def render(self, rect: rl.Rectangle): - gui_text_box( - rect, - "Demo Home Layout", - font_size=170, - color=rl.WHITE, - alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER, - alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE, + self._update_layout_rects(rect) + + current_time = time.time() + if current_time - self.last_refresh >= REFRESH_INTERVAL: + self._refresh() + self.last_refresh = current_time + + self._handle_input() + self._render_header() + + # Render content based on current state + if self.current_state == HomeLayoutState.HOME: + self._render_home_content() + elif self.current_state == HomeLayoutState.UPDATE: + self._render_update_view() + elif self.current_state == HomeLayoutState.ALERTS: + self._render_alerts_view() + + def _update_layout_rects(self, rect: rl.Rectangle): + self.header_rect = rl.Rectangle( + rect.x + CONTENT_MARGIN, rect.y + CONTENT_MARGIN, rect.width - 2 * CONTENT_MARGIN, HEADER_HEIGHT ) + + content_y = rect.y + CONTENT_MARGIN + HEADER_HEIGHT + SPACING + content_height = rect.height - CONTENT_MARGIN - HEADER_HEIGHT - SPACING - CONTENT_MARGIN + + self.content_rect = rl.Rectangle( + rect.x + CONTENT_MARGIN, content_y, rect.width - 2 * CONTENT_MARGIN, content_height + ) + + left_width = self.content_rect.width - RIGHT_COLUMN_WIDTH - SPACING + + self.left_column_rect = rl.Rectangle(self.content_rect.x, self.content_rect.y, left_width, self.content_rect.height) + + self.right_column_rect = rl.Rectangle( + self.content_rect.x + left_width + SPACING, self.content_rect.y, RIGHT_COLUMN_WIDTH, self.content_rect.height + ) + + self.update_notif_rect.x = self.header_rect.x + self.update_notif_rect.y = self.header_rect.y + (self.header_rect.height - 60) // 2 + + notif_x = self.header_rect.x + (220 if self.update_available else 0) + self.alert_notif_rect.x = notif_x + self.alert_notif_rect.y = self.header_rect.y + (self.header_rect.height - 60) // 2 + + def _handle_input(self): + if not rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): + return + + mouse_pos = rl.get_mouse_position() + + if self.update_available and rl.check_collision_point_rec(mouse_pos, self.update_notif_rect): + self._set_state(HomeLayoutState.UPDATE) + return + + if self.alert_count > 0 and rl.check_collision_point_rec(mouse_pos, self.alert_notif_rect): + self._set_state(HomeLayoutState.ALERTS) + return + + # Content area input handling + if self.current_state == HomeLayoutState.UPDATE: + self.update_alert.handle_input(mouse_pos, True) + elif self.current_state == HomeLayoutState.ALERTS: + self.offroad_alert.handle_input(mouse_pos, True) + + def _render_header(self): + font = gui_app.font(FontWeight.MEDIUM) + + # Update notification button + if self.update_available: + # Highlight if currently viewing updates + highlight_color = rl.Color(255, 140, 40, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(255, 102, 0, 255) + rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color) + + text = "UPDATE" + text_width = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE).x + text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_width) // 2 + text_y = self.update_notif_rect.y + (self.update_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2 + rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE) + + # Alert notification button + if self.alert_count > 0: + # Highlight if currently viewing alerts + highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255) + rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color) + + alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}" + text_width = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE).x + text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_width) // 2 + text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2 + rl.draw_text_ex(font, alert_text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE) + + # Version text (right aligned) + version_text = self._get_version_text() + text_width = measure_text_cached(gui_app.font(FontWeight.NORMAL), version_text, 48).x + version_x = self.header_rect.x + self.header_rect.width - text_width + version_y = self.header_rect.y + (self.header_rect.height - 48) // 2 + rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), version_text, rl.Vector2(int(version_x), int(version_y)), 48, 0, DEFAULT_TEXT_COLOR) + + def _render_home_content(self): + self._render_left_column() + self._render_right_column() + + def _render_update_view(self): + self.update_alert.render(self.content_rect) + + def _render_alerts_view(self): + self.offroad_alert.render(self.content_rect) + + def _render_left_column(self): + rl.draw_rectangle_rounded(self.left_column_rect, 0.02, 10, PRIME_BG_COLOR) + gui_label(self.left_column_rect, "Prime Widget", 48, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + def _render_right_column(self): + widget_height = (self.right_column_rect.height - SPACING) // 2 + + exp_rect = rl.Rectangle( + self.right_column_rect.x, self.right_column_rect.y, self.right_column_rect.width, widget_height + ) + rl.draw_rectangle_rounded(exp_rect, 0.02, 10, PRIME_BG_COLOR) + gui_label(exp_rect, "Experimental Mode", 36, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + setup_rect = rl.Rectangle( + self.right_column_rect.x, + self.right_column_rect.y + widget_height + SPACING, + self.right_column_rect.width, + widget_height, + ) + rl.draw_rectangle_rounded(setup_rect, 0.02, 10, PRIME_BG_COLOR) + gui_label(setup_rect, "Setup", 36, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) + + def _refresh(self): + self.update_available = self.update_alert.refresh() + self.alert_count = self.offroad_alert.refresh() + self._update_state_priority(self.update_available, self.alert_count > 0) + + def _update_state_priority(self, update_available: bool, alerts_present: bool): + current_state = self.current_state + + if not update_available and not alerts_present: + self.current_state = HomeLayoutState.HOME + elif update_available and (current_state == HomeLayoutState.HOME or (not alerts_present and current_state == HomeLayoutState.ALERTS)): + self.current_state = HomeLayoutState.UPDATE + elif alerts_present and (current_state == HomeLayoutState.HOME or (not update_available and current_state == HomeLayoutState.UPDATE)): + self.current_state = HomeLayoutState.ALERTS + + def _get_version_text(self) -> str: + brand = "openpilot" + description = self.params.get("UpdaterCurrentDescription", encoding='utf-8') + return f"{brand} {description}" if description else brand diff --git a/selfdrive/ui/widgets/offroad_alerts.py b/selfdrive/ui/widgets/offroad_alerts.py new file mode 100644 index 0000000000..df88dc8554 --- /dev/null +++ b/selfdrive/ui/widgets/offroad_alerts.py @@ -0,0 +1,328 @@ +import json +import pyray as rl +from abc import ABC, abstractmethod +from collections.abc import Callable +from dataclasses import dataclass +from openpilot.common.params import Params +from openpilot.system.hardware import HARDWARE +from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel +from openpilot.system.ui.lib.wrap_text import wrap_text +from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.lib.application import gui_app, FontWeight + +class AlertColors: + HIGH_SEVERITY = rl.Color(226, 44, 44, 255) + LOW_SEVERITY = rl.Color(41, 41, 41, 255) + BACKGROUND = rl.Color(57, 57, 57, 255) + BUTTON = rl.WHITE + BUTTON_TEXT = rl.BLACK + SNOOZE_BG = rl.Color(79, 79, 79, 255) + TEXT = rl.WHITE + + +class AlertConstants: + BUTTON_SIZE = (400, 125) + SNOOZE_BUTTON_SIZE = (550, 125) + REBOOT_BUTTON_SIZE = (600, 125) + MARGIN = 50 + SPACING = 30 + FONT_SIZE = 48 + BORDER_RADIUS = 30 + ALERT_HEIGHT = 120 + ALERT_SPACING = 20 + + +@dataclass +class AlertData: + key: str + text: str + severity: int + visible: bool = False + + +class AbstractAlert(ABC): + def __init__(self, has_reboot_btn: bool = False): + self.params = Params() + self.has_reboot_btn = has_reboot_btn + self.dismiss_callback: Callable | None = None + + self.dismiss_btn_rect = rl.Rectangle(0, 0, *AlertConstants.BUTTON_SIZE) + self.snooze_btn_rect = rl.Rectangle(0, 0, *AlertConstants.SNOOZE_BUTTON_SIZE) + self.reboot_btn_rect = rl.Rectangle(0, 0, *AlertConstants.REBOOT_BUTTON_SIZE) + + self.snooze_visible = False + self.content_rect = rl.Rectangle(0, 0, 0, 0) + self.scroll_panel_rect = rl.Rectangle(0, 0, 0, 0) + self.scroll_panel = GuiScrollPanel() + + def set_dismiss_callback(self, callback: Callable): + self.dismiss_callback = callback + + @abstractmethod + def refresh(self) -> bool: + pass + + @abstractmethod + def get_content_height(self) -> float: + pass + + def handle_input(self, mouse_pos: rl.Vector2, mouse_clicked: bool) -> bool: + # TODO: fix scroll_panel.is_click_valid() + if not mouse_clicked: + return False + + if rl.check_collision_point_rec(mouse_pos, self.dismiss_btn_rect): + if self.dismiss_callback: + self.dismiss_callback() + return True + + if self.snooze_visible and rl.check_collision_point_rec(mouse_pos, self.snooze_btn_rect): + self.params.put_bool("SnoozeUpdate", True) + if self.dismiss_callback: + self.dismiss_callback() + return True + + if self.has_reboot_btn and rl.check_collision_point_rec(mouse_pos, self.reboot_btn_rect): + HARDWARE.reboot() + return True + + return False + + def render(self, rect: rl.Rectangle): + rl.draw_rectangle_rounded(rect, AlertConstants.BORDER_RADIUS / rect.width, 10, AlertColors.BACKGROUND) + + footer_height = AlertConstants.BUTTON_SIZE[1] + AlertConstants.SPACING + content_height = rect.height - 2 * AlertConstants.MARGIN - footer_height + + self.content_rect = rl.Rectangle( + rect.x + AlertConstants.MARGIN, + rect.y + AlertConstants.MARGIN, + rect.width - 2 * AlertConstants.MARGIN, + content_height, + ) + self.scroll_panel_rect = rl.Rectangle( + self.content_rect.x, self.content_rect.y, self.content_rect.width, self.content_rect.height + ) + + self._render_scrollable_content() + self._render_footer(rect) + + def _render_scrollable_content(self): + content_total_height = self.get_content_height() + content_bounds = rl.Rectangle(0, 0, self.scroll_panel_rect.width, content_total_height) + scroll_offset = self.scroll_panel.handle_scroll(self.scroll_panel_rect, content_bounds) + + rl.begin_scissor_mode( + int(self.scroll_panel_rect.x), + int(self.scroll_panel_rect.y), + int(self.scroll_panel_rect.width), + int(self.scroll_panel_rect.height), + ) + + content_rect_with_scroll = rl.Rectangle( + self.scroll_panel_rect.x, + self.scroll_panel_rect.y + scroll_offset.y, + self.scroll_panel_rect.width, + content_total_height, + ) + + self._render_content(content_rect_with_scroll) + rl.end_scissor_mode() + + @abstractmethod + def _render_content(self, content_rect: rl.Rectangle): + pass + + def _render_footer(self, rect: rl.Rectangle): + footer_y = rect.y + rect.height - AlertConstants.MARGIN - AlertConstants.BUTTON_SIZE[1] + font = gui_app.font(FontWeight.MEDIUM) + + self.dismiss_btn_rect.x = rect.x + AlertConstants.MARGIN + self.dismiss_btn_rect.y = footer_y + rl.draw_rectangle_rounded(self.dismiss_btn_rect, 0.3, 10, AlertColors.BUTTON) + + text = "Close" + text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x + text_x = self.dismiss_btn_rect.x + (AlertConstants.BUTTON_SIZE[0] - text_width) // 2 + text_y = self.dismiss_btn_rect.y + (AlertConstants.BUTTON_SIZE[1] - AlertConstants.FONT_SIZE) // 2 + rl.draw_text_ex( + font, text, rl.Vector2(int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.BUTTON_TEXT + ) + + if self.snooze_visible: + self.snooze_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.SNOOZE_BUTTON_SIZE[0] + self.snooze_btn_rect.y = footer_y + rl.draw_rectangle_rounded(self.snooze_btn_rect, 0.3, 10, AlertColors.SNOOZE_BG) + + text = "Snooze Update" + text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x + text_x = self.snooze_btn_rect.x + (AlertConstants.SNOOZE_BUTTON_SIZE[0] - text_width) // 2 + text_y = self.snooze_btn_rect.y + (AlertConstants.SNOOZE_BUTTON_SIZE[1] - AlertConstants.FONT_SIZE) // 2 + rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.TEXT) + + elif self.has_reboot_btn: + self.reboot_btn_rect.x = rect.x + rect.width - AlertConstants.MARGIN - AlertConstants.REBOOT_BUTTON_SIZE[0] + self.reboot_btn_rect.y = footer_y + rl.draw_rectangle_rounded(self.reboot_btn_rect, 0.3, 10, AlertColors.BUTTON) + + text = "Reboot and Update" + text_width = measure_text_cached(font, text, AlertConstants.FONT_SIZE).x + text_x = self.reboot_btn_rect.x + (AlertConstants.REBOOT_BUTTON_SIZE[0] - text_width) // 2 + text_y = self.reboot_btn_rect.y + (AlertConstants.REBOOT_BUTTON_SIZE[1] - AlertConstants.FONT_SIZE) // 2 + rl.draw_text_ex( + font, text, rl.Vector2(int(text_x), int(text_y)), AlertConstants.FONT_SIZE, 0, AlertColors.BUTTON_TEXT + ) + + +class OffroadAlert(AbstractAlert): + def __init__(self): + super().__init__(has_reboot_btn=False) + self.sorted_alerts: list[AlertData] = [] + + def refresh(self): + if not self.sorted_alerts: + self._build_alerts() + + active_count = 0 + connectivity_needed = False + + for alert_data in self.sorted_alerts: + text = "" + bytes_data = self.params.get(alert_data.key) + + if bytes_data: + try: + alert_json = json.loads(bytes_data) + text = alert_json.get("text", "").replace("{}", alert_json.get("extra", "")) + except json.JSONDecodeError: + text = "" + + alert_data.text = text + alert_data.visible = bool(text) + + if alert_data.visible: + active_count += 1 + + if alert_data.key == "Offroad_ConnectivityNeeded" and alert_data.visible: + connectivity_needed = True + + self.snooze_visible = connectivity_needed + return active_count + + def get_content_height(self) -> float: + if not self.sorted_alerts: + return 0 + + total_height = 20 + font = gui_app.font(FontWeight.NORMAL) + + for alert_data in self.sorted_alerts: + if not alert_data.visible: + continue + + text_width = int(self.content_rect.width - 90) + wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) + line_count = len(wrapped_lines) + text_height = line_count * (AlertConstants.FONT_SIZE + 5) + alert_item_height = max(text_height + 40, AlertConstants.ALERT_HEIGHT) + total_height += alert_item_height + AlertConstants.ALERT_SPACING + + if total_height > 20: + total_height = total_height - AlertConstants.ALERT_SPACING + 20 + + return total_height + + def _build_alerts(self): + self.sorted_alerts = [] + try: + with open("../selfdrived/alerts_offroad.json", "rb") as f: + alerts_config = json.load(f) + for key, config in sorted(alerts_config.items(), key=lambda x: x[1].get("severity", 0), reverse=True): + severity = config.get("severity", 0) + alert_data = AlertData(key=key, text="", severity=severity) + self.sorted_alerts.append(alert_data) + except (FileNotFoundError, json.JSONDecodeError): + pass + + def _render_content(self, content_rect: rl.Rectangle): + y_offset = 20 + font = gui_app.font(FontWeight.NORMAL) + + for alert_data in self.sorted_alerts: + if not alert_data.visible: + continue + + bg_color = AlertColors.HIGH_SEVERITY if alert_data.severity > 0 else AlertColors.LOW_SEVERITY + text_width = int(content_rect.width - 90) + wrapped_lines = wrap_text(font, alert_data.text, AlertConstants.FONT_SIZE, text_width) + line_count = len(wrapped_lines) + text_height = line_count * (AlertConstants.FONT_SIZE + 5) + alert_item_height = max(text_height + 40, AlertConstants.ALERT_HEIGHT) + + alert_rect = rl.Rectangle( + content_rect.x + 10, + content_rect.y + y_offset, + content_rect.width - 30, + alert_item_height, + ) + + rl.draw_rectangle_rounded(alert_rect, 0.2, 10, bg_color) + + text_x = alert_rect.x + 30 + text_y = alert_rect.y + 20 + + for i, line in enumerate(wrapped_lines): + rl.draw_text_ex( + font, + line, + rl.Vector2(text_x, text_y + i * (AlertConstants.FONT_SIZE + 5)), + AlertConstants.FONT_SIZE, + 0, + AlertColors.TEXT, + ) + + y_offset += alert_item_height + AlertConstants.ALERT_SPACING + + +class UpdateAlert(AbstractAlert): + def __init__(self): + super().__init__(has_reboot_btn=True) + self.release_notes = "" + self._wrapped_release_notes = "" + self._cached_content_height: float = 0.0 + + def refresh(self) -> bool: + update_available: bool = self.params.get_bool("UpdateAvailable") + if update_available: + self.release_notes = self.params.get("UpdaterNewReleaseNotes", encoding='utf-8') + self._cached_content_height = 0 + + return update_available + + def get_content_height(self) -> float: + if not self.release_notes: + return 100 + + if self._cached_content_height == 0: + self._wrapped_release_notes = self.release_notes + size = measure_text_cached(gui_app.font(FontWeight.NORMAL), self._wrapped_release_notes, AlertConstants.FONT_SIZE) + self._cached_content_height = max(size.y + 60, 100) + + return self._cached_content_height + + def _render_content(self, content_rect: rl.Rectangle): + if self.release_notes: + rl.draw_text_ex( + gui_app.font(FontWeight.NORMAL), + self._wrapped_release_notes, + rl.Vector2(content_rect.x + 30, content_rect.y + 30), + AlertConstants.FONT_SIZE, + 0.0, + AlertColors.TEXT, + ) + else: + no_notes_text = "No release notes available." + text_width = rl.measure_text(no_notes_text, AlertConstants.FONT_SIZE) + text_x = content_rect.x + (content_rect.width - text_width) // 2 + text_y = content_rect.y + 50 + rl.draw_text(no_notes_text, int(text_x), int(text_y), AlertConstants.FONT_SIZE, AlertColors.TEXT) From 3a622cbe255735d02b13227b4c910dffacf6e175 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Fri, 6 Jun 2025 04:16:38 +0800 Subject: [PATCH 455/477] ui: add WiFi manager to settings (#35454) add WiFi manager to settings --- selfdrive/ui/layouts/network.py | 19 +++++++++++++++++++ selfdrive/ui/layouts/settings/settings.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 selfdrive/ui/layouts/network.py diff --git a/selfdrive/ui/layouts/network.py b/selfdrive/ui/layouts/network.py new file mode 100644 index 0000000000..bf52f18876 --- /dev/null +++ b/selfdrive/ui/layouts/network.py @@ -0,0 +1,19 @@ +import pyray as rl +from openpilot.system.ui.lib.wifi_manager import WifiManagerWrapper +from openpilot.system.ui.widgets.network import WifiManagerUI + + +class NetworkLayout: + def __init__(self): + self.wifi_manager = WifiManagerWrapper() + self.wifi_ui = WifiManagerUI(self.wifi_manager) + + def render(self, rect: rl.Rectangle): + self.wifi_ui.render(rect) + + @property + def require_full_screen(self): + return self.wifi_ui.require_full_screen + + def shutdown(self): + self.wifi_manager.shutdown() diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 849a26f0dd..b3d28b0adf 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -9,6 +9,7 @@ from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_text_box +from openpilot.selfdrive.ui.layouts.network import NetworkLayout # Import individual panels @@ -58,7 +59,7 @@ class SettingsLayout: PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout(), rl.Rectangle(0, 0, 0, 0)), PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout(), rl.Rectangle(0, 0, 0, 0)), PanelType.FIREHOSE: PanelInfo("Firehose", None, rl.Rectangle(0, 0, 0, 0)), - PanelType.NETWORK: PanelInfo("Network", None, rl.Rectangle(0, 0, 0, 0)), + PanelType.NETWORK: PanelInfo("Network", NetworkLayout(), rl.Rectangle(0, 0, 0, 0)), PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout(), rl.Rectangle(0, 0, 0, 0)), } From 2000f9aff3477e6eaaf9dea4006acd93e59bc816 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Thu, 5 Jun 2025 14:37:12 -0700 Subject: [PATCH 456/477] raylib: ban non-cached measure_text_ex (#35462) * add * use it * Update pyproject.toml * many more * comment --- pyproject.toml | 1 + selfdrive/ui/layouts/settings/settings.py | 5 +++-- system/ui/lib/button.py | 3 ++- system/ui/lib/inputbox.py | 7 ++++--- system/ui/lib/label.py | 7 ++++--- system/ui/lib/text_measure.py | 3 ++- system/ui/spinner.py | 3 ++- system/ui/text.py | 3 ++- 8 files changed, 20 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b9a9fcab55..ce763d152d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -259,6 +259,7 @@ lint.flake8-implicit-str-concat.allow-multiline = false "tools".msg = "Use openpilot.tools" "pytest.main".msg = "pytest.main requires special handling that is easy to mess up!" "unittest".msg = "Use pytest" +"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure" [tool.coverage.run] concurrency = ["multiprocessing", "thread"] diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index b3d28b0adf..1afa4f8f4a 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -9,6 +9,7 @@ from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.label import gui_text_box +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.selfdrive.ui.layouts.network import NetworkLayout # Import individual panels @@ -97,7 +98,7 @@ class SettingsLayout: close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color) - close_text_size = rl.measure_text_ex(self._font_bold, SETTINGS_CLOSE_TEXT, 140, 0) + close_text_size = measure_text_cached(self._font_bold, SETTINGS_CLOSE_TEXT, 140) close_text_pos = rl.Vector2( close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2, close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2, @@ -124,7 +125,7 @@ class SettingsLayout: is_selected = panel_type == self._current_panel text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL # Draw button text (right-aligned) - text_size = rl.measure_text_ex(self._font_medium, panel_info.name, 65, 0) + text_size = measure_text_cached(self._font_medium, panel_info.name, 65) text_pos = rl.Vector2( button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2 ) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index 9ca82c9732..024e360448 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -1,6 +1,7 @@ import pyray as rl from enum import IntEnum from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.text_measure import measure_text_cached class ButtonStyle(IntEnum): @@ -99,7 +100,7 @@ def gui_button( # Handle icon and text positioning font = gui_app.font(font_weight) - text_size = rl.measure_text_ex(font, text, font_size, 0) + text_size = measure_text_cached(font, text, font_size) text_pos = rl.Vector2(0, rect.y + (rect.height - text_size.y) // 2) # Vertical centering # Draw icon if provided diff --git a/system/ui/lib/inputbox.py b/system/ui/lib/inputbox.py index 367e0dc7e4..ae036c03c2 100644 --- a/system/ui/lib/inputbox.py +++ b/system/ui/lib/inputbox.py @@ -1,6 +1,7 @@ import pyray as rl import time from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.text_measure import measure_text_cached PASSWORD_MASK_CHAR = "•" @@ -60,7 +61,7 @@ class InputBox: padding = 10 if self._cursor_position > 0: - cursor_x = rl.measure_text_ex(font, display_text[: self._cursor_position], self._font_size, 0).x + cursor_x = measure_text_cached(font, display_text[: self._cursor_position], self._font_size).x else: cursor_x = 0 @@ -141,7 +142,7 @@ class InputBox: if self._show_cursor: cursor_x = rect.x + padding if len(display_text) > 0 and self._cursor_position > 0: - cursor_x += rl.measure_text_ex(font, display_text[: self._cursor_position], font_size, 0).x + cursor_x += measure_text_cached(font, display_text[: self._cursor_position], font_size).x # Apply text offset to cursor position cursor_x -= self._text_offset @@ -182,7 +183,7 @@ class InputBox: min_distance = float('inf') for i in range(len(self._input_text) + 1): - char_width = rl.measure_text_ex(font, display_text[:i], font_size, 0).x + char_width = measure_text_cached(font, display_text[:i], font_size).x distance = abs(relative_x - char_width) if distance < min_distance: min_distance = distance diff --git a/system/ui/lib/label.py b/system/ui/lib/label.py index 00a4eda7c6..82533660de 100644 --- a/system/ui/lib/label.py +++ b/system/ui/lib/label.py @@ -1,5 +1,6 @@ import pyray as rl from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.utils import GuiStyleContext @@ -14,7 +15,7 @@ def gui_label( elide_right: bool = True ): font = gui_app.font(font_weight) - text_size = rl.measure_text_ex(font, text, font_size, 0) + text_size = measure_text_cached(font, text, font_size) display_text = text # Elide text to fit within the rectangle @@ -24,13 +25,13 @@ def gui_label( while left < right: mid = (left + right) // 2 candidate = text[:mid] + ellipsis - candidate_size = rl.measure_text_ex(font, candidate, font_size, 0) + candidate_size = measure_text_cached(font, candidate, font_size) if candidate_size.x <= rect.width: left = mid + 1 else: right = mid display_text = text[: left - 1] + ellipsis if left > 0 else ellipsis - text_size = rl.measure_text_ex(font, display_text, font_size, 0) + text_size = measure_text_cached(font, display_text, font_size) # Calculate horizontal position based on alignment text_x = rect.x + { diff --git a/system/ui/lib/text_measure.py b/system/ui/lib/text_measure.py index 9cdd566409..c172f94251 100644 --- a/system/ui/lib/text_measure.py +++ b/system/ui/lib/text_measure.py @@ -4,10 +4,11 @@ _cache: dict[int, rl.Vector2] = {} def measure_text_cached(font: rl.Font, text: str, font_size: int, spacing: int = 0) -> rl.Vector2: + """Caches text measurements to avoid redundant calculations.""" key = hash((font.texture.id, text, font_size, spacing)) if key in _cache: return _cache[key] - result = rl.measure_text_ex(font, text, font_size, spacing) + result = rl.measure_text_ex(font, text, font_size, spacing) # noqa: TID251 _cache[key] = result return result diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 2af24c4e51..3825f1300d 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -4,6 +4,7 @@ import threading import time from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.window import BaseWindow from openpilot.system.ui.text import wrap_text @@ -78,7 +79,7 @@ class SpinnerRenderer: rl.draw_rectangle_rounded(bar, 1, 10, rl.WHITE) elif wrapped_lines: for i, line in enumerate(wrapped_lines): - text_size = rl.measure_text_ex(gui_app.font(), line, FONT_SIZE, 0.0) + text_size = measure_text_cached(gui_app.font(), line, FONT_SIZE) rl.draw_text_ex(gui_app.font(), line, rl.Vector2(center.x - text_size.x / 2, y_pos + i * LINE_HEIGHT), FONT_SIZE, 0.0, rl.WHITE) diff --git a/system/ui/text.py b/system/ui/text.py index 82e64d836f..63d00fbde3 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -3,6 +3,7 @@ import re import time import pyray as rl from openpilot.system.hardware import HARDWARE, PC +from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app @@ -33,7 +34,7 @@ def wrap_text(text, font_size, max_width): while len(words): word = words.pop(0) test_line = current_line + word + (words.pop(0) if words else "") - if rl.measure_text_ex(font, test_line, font_size, 0).x <= max_width: + if measure_text_cached(font, test_line, font_size).x <= max_width: current_line = test_line else: lines.append(current_line) From ce9d1683a572aae708be7dea1dd988fe580bd834 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 5 Jun 2025 16:58:44 -0700 Subject: [PATCH 457/477] bump to 0.9.10 --- RELEASES.md | 3 +++ common/version.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 5d74689a2d..91652d6dc7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,6 @@ +Version 0.9.10 (2025-06-30) +======================== + Version 0.9.9 (2025-05-23) ======================== * New driving model diff --git a/common/version.h b/common/version.h index 6351a5b3ff..67b69da7c7 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.9.9" +#define COMMA_VERSION "0.9.10" From 445eda9d5de2540a6d9e46d6f7ac34557d5692a5 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 5 Jun 2025 23:41:04 -0400 Subject: [PATCH 458/477] Bump submodules --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index 4ff297f230..f2b2b9e50d 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4ff297f230a55ab052be0f047d7c3049b1b317be +Subproject commit f2b2b9e50ded6c9024715cbb1f5aa7ef911c792d From 835d6a656e77917d69253dafe206a9b03577fc8e Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Fri, 6 Jun 2025 00:05:20 -0400 Subject: [PATCH 459/477] Bump submodules --- opendbc_repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendbc_repo b/opendbc_repo index f2b2b9e50d..202a26d943 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit f2b2b9e50ded6c9024715cbb1f5aa7ef911c792d +Subproject commit 202a26d94315edaa981ce80ed73c6022d2d66997 From 8849f44e4190f55673af9256df89a937f5c7a6dc Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 6 Jun 2025 14:33:33 -0400 Subject: [PATCH 460/477] ui: sp spinner (#984) * sp spinner * fine!! --------- Co-authored-by: DevTekVE --- .../assets/images/spinner_sunnypilot.png | 3 +++ system/ui/spinner.py | 4 +++- system/ui/sunnypilot/lib/application.py | 21 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 sunnypilot/selfdrive/assets/images/spinner_sunnypilot.png create mode 100644 system/ui/sunnypilot/lib/application.py diff --git a/sunnypilot/selfdrive/assets/images/spinner_sunnypilot.png b/sunnypilot/selfdrive/assets/images/spinner_sunnypilot.png new file mode 100644 index 0000000000..edc5c6e5c2 --- /dev/null +++ b/sunnypilot/selfdrive/assets/images/spinner_sunnypilot.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9dbacb55581a5ea8cbcd5cea0560ec21d96ac7185463a40fff0f81bebf044ffa +size 23187 diff --git a/system/ui/spinner.py b/system/ui/spinner.py index 3825f1300d..ed605885b0 100755 --- a/system/ui/spinner.py +++ b/system/ui/spinner.py @@ -8,6 +8,8 @@ from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.window import BaseWindow from openpilot.system.ui.text import wrap_text +from openpilot.system.ui.sunnypilot.lib.application import gui_app_sp + # Constants PROGRESS_BAR_WIDTH = 1000 PROGRESS_BAR_HEIGHT = 20 @@ -25,7 +27,7 @@ def clamp(value, min_value, max_value): class SpinnerRenderer: def __init__(self): - self._comma_texture = gui_app.texture("images/spinner_comma.png", TEXTURE_SIZE, TEXTURE_SIZE) + self._comma_texture = gui_app_sp.sp_texture("images/spinner_sunnypilot.png", TEXTURE_SIZE, TEXTURE_SIZE) self._spinner_texture = gui_app.texture("images/spinner_track.png", TEXTURE_SIZE, TEXTURE_SIZE, alpha_premultiply=True) self._rotation = 0.0 self._progress: int | None = None diff --git a/system/ui/sunnypilot/lib/application.py b/system/ui/sunnypilot/lib/application.py new file mode 100644 index 0000000000..10227ce9fd --- /dev/null +++ b/system/ui/sunnypilot/lib/application.py @@ -0,0 +1,21 @@ +from openpilot.system.ui.lib.application import GuiApplication +from importlib.resources import as_file, files + +ASSETS_DIR_SP = files("openpilot.sunnypilot.selfdrive").joinpath("assets") + +class GuiApplicationSP(GuiApplication): + + def __init__(self, width: int, height: int): + super().__init__(width, height) + + def sp_texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): + cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" + if cache_key in self._textures: + return self._textures[cache_key] + + with as_file(ASSETS_DIR_SP.joinpath(asset_path)) as fspath: + texture_obj = self._load_texture_from_image(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio) + self._textures[cache_key] = texture_obj + return texture_obj + +gui_app_sp = GuiApplicationSP(2160, 1080) From 65c512acc00f9c4b6c485ef7b08fe26ec35ec279 Mon Sep 17 00:00:00 2001 From: James Vecellio-Grant <159560811+Discountchubbs@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:43:10 -0700 Subject: [PATCH 461/477] ui/models panel: liveDelay Toggle (#971) * Model panel * Get this outta here and use sunnylink instead * Remove space * 'not' because we want gas gating to use e2e/mpc blend * Add toggles to models_panel.cc * "keep enabled for stock behavior" * Add this here * Cloudlog result * change cloudlog to debug, and add latsmooth to steer actuator delay. Need to edit json. Will do locally so when this is merged its a simple ready to go push. * Cleanup * Update longitudinal_planner.py * Remove gasgating for now.. may need to be placed in model --------- Co-authored-by: DevTekVE --- common/params_keys.h | 5 +++- .../qt/offroad/settings/models_panel.cc | 7 +++++ sunnypilot/modeld/modeld.py | 6 ++++- sunnypilot/modeld_v2/modeld.py | 6 ++++- sunnypilot/models/modeld_lagd.py | 26 +++++++++++++++++++ system/manager/manager.py | 1 + 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 sunnypilot/models/modeld_lagd.py diff --git a/common/params_keys.h b/common/params_keys.h index 6474fb5840..4fd98c1b38 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -71,7 +71,7 @@ inline static std::unordered_map keys = { {"LastPowerDropDetected", CLEAR_ON_MANAGER_START}, {"LastUpdateException", CLEAR_ON_MANAGER_START}, {"LastUpdateTime", PERSISTENT}, - {"LiveDelay", PERSISTENT}, + {"LiveDelay", PERSISTENT | BACKUP}, {"LiveParameters", PERSISTENT}, {"LiveParametersV2", PERSISTENT}, {"LiveTorqueParameters", PERSISTENT | DONT_LOG}, @@ -170,4 +170,7 @@ inline static std::unordered_map keys = { {"HyundaiLongitudinalTuning", PERSISTENT}, {"DynamicExperimentalControl", PERSISTENT}, + + // model panel params + {"LagdToggle", PERSISTENT | BACKUP}, }; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index d33cb550ce..3c8d034c5a 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -31,6 +31,13 @@ ModelsPanel::ModelsPanel(QWidget *parent) : QWidget(parent) { }); connect(uiStateSP(), &UIStateSP::uiUpdate, this, &ModelsPanel::updateLabels); list->addItem(currentModelLblBtn); + + // LiveDelay toggle + list->addItem(new ParamControlSP("LagdToggle", + tr("Live Learning Steer Delay"), + tr("Enable this for the car to learn and adapt its steering response time. " + "Disable to use a fixed steering response time. Keeping this on provides the stock openpilot experience."), + "../assets/offroad/icon_shell.png")); } diff --git a/sunnypilot/modeld/modeld.py b/sunnypilot/modeld/modeld.py index 2ac3f5700e..d4bd96ff66 100755 --- a/sunnypilot/modeld/modeld.py +++ b/sunnypilot/modeld/modeld.py @@ -25,6 +25,7 @@ from openpilot.sunnypilot.modeld.parse_model_outputs import Parser from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState from openpilot.sunnypilot.modeld.constants import ModelConstants, Plan from openpilot.sunnypilot.models.helpers import get_active_bundle, get_model_path, load_metadata, prepare_inputs, load_meta_constants +from openpilot.sunnypilot.models.modeld_lagd import ModeldLagd from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext @@ -201,8 +202,9 @@ def main(demo=False): cloudlog.info("modeld got CarParams: %s", CP.brand) + modeld_lagd = ModeldLagd() + # Enable lagd support for sunnypilot modeld - steer_delay = sm["liveDelay"].lateralDelay + model.LAT_SMOOTH_SECONDS long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -246,6 +248,8 @@ def main(demo=False): v_ego = sm["carState"].vEgo is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId + steer_delay = modeld_lagd.lagd_main(CP, sm, model) + if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] diff --git a/sunnypilot/modeld_v2/modeld.py b/sunnypilot/modeld_v2/modeld.py index 4c8acafcad..fe322f462e 100755 --- a/sunnypilot/modeld_v2/modeld.py +++ b/sunnypilot/modeld_v2/modeld.py @@ -23,6 +23,7 @@ from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFr from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants from openpilot.sunnypilot.models.helpers import get_active_bundle +from openpilot.sunnypilot.models.modeld_lagd import ModeldLagd from openpilot.sunnypilot.models.runners.helpers import get_model_runner PROCESS_NAME = "selfdrive.modeld.modeld" @@ -238,6 +239,9 @@ def main(demo=False): CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) + + modeld_lagd = ModeldLagd() + # TODO Move smooth seconds to action function long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS prev_action = log.ModelDataV2.Action() @@ -282,7 +286,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - steer_delay = sm["liveDelay"].lateralDelay + model.LAT_SMOOTH_SECONDS + steer_delay = modeld_lagd.lagd_main(CP, sm, model) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))] diff --git a/sunnypilot/models/modeld_lagd.py b/sunnypilot/models/modeld_lagd.py new file mode 100644 index 0000000000..9e538123a8 --- /dev/null +++ b/sunnypilot/models/modeld_lagd.py @@ -0,0 +1,26 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog + + +class ModeldLagd: + def __init__(self): + self.params = Params() + + def lagd_main(self, CP, sm, model): + if self.params.get_bool("LagdToggle"): + lateral_delay = sm["liveDelay"].lateralDelay + lat_smooth = model.LAT_SMOOTH_SECONDS + result = lateral_delay + lat_smooth + cloudlog.debug(f"LAGD USING LIVE DELAY: {lateral_delay:.3f} + {lat_smooth:.3f} = {result:.3f}") + return result + steer_actuator_delay = CP.steerActuatorDelay + lat_smooth = model.LAT_SMOOTH_SECONDS + result = (steer_actuator_delay + 0.2) + lat_smooth + cloudlog.debug(f"LAGD USING STEER ACTUATOR: {steer_actuator_delay:.3f} + 0.2 + {lat_smooth:.3f} = {result:.3f}") + return result diff --git a/system/manager/manager.py b/system/manager/manager.py index 46b849108c..13048a8851 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -48,6 +48,7 @@ def manager_init() -> None: ("BlinkerPauseLateralControl", "0"), ("DynamicExperimentalControl", "0"), ("HyundaiLongitudinalTuning", "0"), + ("LagdToggle", "1"), ("Mads", "1"), ("MadsMainCruiseAllowed", "1"), ("MadsSteeringMode", "0"), From ab68ce99bada7edae852a264131aebeb486616ea Mon Sep 17 00:00:00 2001 From: DevTekVE Date: Fri, 6 Jun 2025 20:53:58 +0200 Subject: [PATCH 462/477] chore: Update CODEOWNERS with new ownership rules (#982) Update CODEOWNERS with new ownership rules Expand ownership to additional paths for better code coverage and clarity. Newly added owners include @Discountchubbs and @devtekve for specified directories and files. This ensures proper oversight and maintenance of critical areas. --- .github/CODEOWNERS | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 65d2f5588e..e8f1b7eb8b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,11 @@ * @sunnypilot/dev-internal /.github/ @devtekve @sunnyhaibin -/release/ci/ @devtekve @sunnyhaibin \ No newline at end of file +/release/ci/ @devtekve @sunnyhaibin +/tinygrad_repo @devtekve @Discountchubbs +/tinygrad/ @devtekve @Discountchubbs +/selfdrive/controls/lib/longitudinal_planner.py @devtekve @Discountchubbs +/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @devtekve @Discountchubbs +/selfdrive/modeld/ @devtekve @Discountchubbs +/sunnypilot/model* @devtekve @Discountchubbs +/sunnypilot/sunnylink/ @devtekve +/system/athena/ @devtekve \ No newline at end of file From 539b5aab75e7f35624799cd9983dd8eadaa46d62 Mon Sep 17 00:00:00 2001 From: royjr Date: Fri, 6 Jun 2025 15:38:16 -0400 Subject: [PATCH 463/477] ui: display different border color for long control without lat (#981) Update ui.cc Co-authored-by: Jason Wen Co-authored-by: DevTekVE --- selfdrive/ui/ui.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 06208638db..a521902f89 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -80,8 +80,12 @@ void UIState::updateStatus() { status = STATUS_OVERRIDE; } else { if (mads.getAvailable()) { - if (mads.getEnabled()) { - status = ss.getEnabled() ? STATUS_ENGAGED : STATUS_LAT_ONLY; + if (mads.getEnabled() && ss.getEnabled()) { + status = STATUS_ENGAGED; + } else if (mads.getEnabled()) { + status = STATUS_LAT_ONLY; + } else if (ss.getEnabled()) { + status = STATUS_LONG_ONLY; } else { status = STATUS_DISENGAGED; } From debba0c9f9dcc74dfc0c902aa92286860c5c7d73 Mon Sep 17 00:00:00 2001 From: royjr Date: Fri, 6 Jun 2025 15:51:05 -0400 Subject: [PATCH 464/477] ui: add brightness controls (#974) * brightness * better brightness * cleanup --------- Co-authored-by: DevTekVE --- common/params_keys.h | 1 + selfdrive/ui/sunnypilot/SConscript | 1 + .../qt/offroad/settings/brightness.cc | 50 +++++++++++++++++++ .../qt/offroad/settings/brightness.h | 25 ++++++++++ .../qt/offroad/settings/device_panel.cc | 5 ++ .../qt/offroad/settings/device_panel.h | 2 + selfdrive/ui/ui.cc | 16 ++++-- system/manager/manager.py | 1 + 8 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h diff --git a/common/params_keys.h b/common/params_keys.h index 4fd98c1b38..a74f6061d0 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -133,6 +133,7 @@ inline static std::unordered_map keys = { {"CarPlatformBundle", PERSISTENT}, {"EnableGithubRunner", PERSISTENT | BACKUP}, {"MaxTimeOffroad", PERSISTENT | BACKUP}, + {"Brightness", PERSISTENT | BACKUP}, {"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION}, {"OffroadMode", CLEAR_ON_MANAGER_START}, {"OffroadMode_Status", CLEAR_ON_MANAGER_START}, diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 796107da77..9a9e395d92 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -24,6 +24,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/lateral_panel.cc", "sunnypilot/qt/offroad/settings/longitudinal_panel.cc", "sunnypilot/qt/offroad/settings/max_time_offroad.cc", + "sunnypilot/qt/offroad/settings/brightness.cc", "sunnypilot/qt/offroad/settings/models_panel.cc", "sunnypilot/qt/offroad/settings/settings.cc", "sunnypilot/qt/offroad/settings/software_panel.cc", diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.cc new file mode 100644 index 0000000000..ffd3ce1d8b --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.cc @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h" + +// Map of Brightness Options +const QMap Brightness::brightness_options = { + {"0", "1"}, // Auto (Dark) + {"1", "0"}, // Auto + {"2", "10"}, + {"3", "20"}, + {"4", "30"}, + {"5", "40"}, + {"6", "50"}, + {"7", "60"}, + {"8", "70"}, + {"9", "80"}, + {"10", "90"}, + {"11", "100"} +}; + +Brightness::Brightness() : OptionControlSP( + "Brightness", + tr("Brightness"), + tr("Overrides the brightness of the device."), + "../assets/offroad/icon_blank.png", + {0, 11}, 1, true, &brightness_options) { + + refresh(); +} + +void Brightness::refresh() { + const int brightness = QString::fromStdString(params.get("Brightness")).toInt(); + + QString label; + if (brightness == 1) { + label = tr("Auto (Dark)"); + } else if (brightness == 0) { + label = tr("Auto"); + } else { + const int value = brightness; + label = QString("%1").arg(value); + } + + setLabel(label); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h new file mode 100644 index 0000000000..20d0a291d9 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" + +class Brightness : public OptionControlSP { + Q_OBJECT + +public: + static const QMap brightness_options; + + Brightness(); + void refresh(); + +private: + Params params; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc index e6855c4b4e..a2c7414460 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.cc @@ -80,6 +80,11 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) { connect(maxTimeOffroad, &OptionControlSP::updateLabels, maxTimeOffroad, &MaxTimeOffroad::refresh); addItem(maxTimeOffroad); + // Brightness + brightness = new Brightness(); + connect(brightness, &OptionControlSP::updateLabels, brightness, &Brightness::refresh); + addItem(brightness); + addItem(device_grid_layout); // offroad mode and power buttons diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h index 7b7412a739..e83801dd29 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h @@ -8,6 +8,7 @@ #pragma once #include "selfdrive/ui/sunnypilot/qt/offroad/settings/max_time_offroad.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/brightness.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" @@ -25,6 +26,7 @@ private: std::map buttons; PushButtonSP *offroadBtn; MaxTimeOffroad *maxTimeOffroad; + Brightness *brightness; const QString alwaysOffroadStyle = R"( PushButtonSP { diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index a521902f89..c9dc70cdff 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -171,6 +171,8 @@ void Device::resetInteractiveTimeout(int timeout) { } void Device::updateBrightness(const UIState &s) { + int brightness; + int brightness_override = QString::fromStdString(Params().get("Brightness")).toInt(); float clipped_brightness = offroad_brightness; if (s.scene.started && s.scene.light_sensor >= 0) { clipped_brightness = s.scene.light_sensor; @@ -182,11 +184,19 @@ void Device::updateBrightness(const UIState &s) { clipped_brightness = std::pow((clipped_brightness + 16.0) / 116.0, 3.0); } - // Scale back to 10% to 100% - clipped_brightness = std::clamp(100.0f * clipped_brightness, 10.0f, 100.0f); + if (brightness_override == 1) { + clipped_brightness = std::clamp(100.0f * clipped_brightness, 1.0f, 100.0f); // Scale back to 1% to 100% + } else if (brightness_override == 0) { + clipped_brightness = std::clamp(100.0f * clipped_brightness, 10.0f, 100.0f); // Scale back to 10% to 100% + } + } + + if (brightness_override == 0 || brightness_override == 1) { + brightness = brightness_filter.update(clipped_brightness); + } else { + brightness = brightness_override; } - int brightness = brightness_filter.update(clipped_brightness); if (!awake) { brightness = 0; } diff --git a/system/manager/manager.py b/system/manager/manager.py index 13048a8851..1d72beeda1 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -54,6 +54,7 @@ def manager_init() -> None: ("MadsSteeringMode", "0"), ("MadsUnifiedEngagementMode", "1"), ("MaxTimeOffroad", "1800"), + ("Brightness", "0"), ("ModelManager_LastSyncTime", "0"), ("ModelManager_ModelsCache", ""), ("NeuralNetworkLateralControl", "0"), From 1979b00cc075bbe2980bf0b52f53905cb2362bec Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:39:26 -0700 Subject: [PATCH 465/477] UI: Visual indicator for blind spot (#834) * Refactor and extend ModelRenderer for custom Sunnypilot logic Refactored `ModelRenderer` to `ModelRendererSP` with enhanced features such as lane line updates, path drawing, and lead management for Sunnypilot. Introduced new methods for model updates, lead drawing, and improved path rendering with experimental mode support. Ensured compatibility by integrating with Sunnypilot-specific HUD and camera components. * Update selfdrive/ui/sunnypilot/qt/onroad/model.cc * Refactor `ModelRenderer` for modularity Moved constants and `get_path_length_idx` function to the header file for reuse and clarity. Updated `drawPath` and related methods to better handle surface dimensions, improving rendering flexibility. Made key functions virtual to allow further customization in derived classes. * Cleaning logic on ModelRenderSP Given that we've refactored slightly the original ModelRender, we no longer need to duplicate the logic on our own implementation * Enable blind spot detection and visualization. Added support for blind spot warnings, including gradient-colored visualizations for left and right blind spots on the on-road UI. Introduced a new "BlindSpot" parameter with related logic for detection and rendering, as well as a settings option for user toggling. * Cleanup format Clean Cleanup and fixes * Let's backup the BlindSpot setting * add false for restart-needed * Add blind spot warning toggle to VisualsPanel Moved blind spot warning toggle from settings.cc to VisualsPanel and implemented support for dynamic parameter updates. This change introduces a dedicated layout for managing visual settings and improves modularity in the settings interface. * Update Blind Spot Warnings setting description Clarified the description to specify that warnings are displayed only if the car supports Blind Spot Monitoring (BSM). This ensures better user understanding of the feature's requirements. * Avoid diff on settings.cc * More cleanup --------- Co-authored-by: DevTekVE Co-authored-by: Jason Wen Co-authored-by: Nayan --- common/params_keys.h | 1 + .../qt/offroad/settings/visuals_panel.cc | 58 +++++++++++++++++++ .../qt/offroad/settings/visuals_panel.h | 12 ++++ selfdrive/ui/sunnypilot/qt/onroad/model.cc | 44 ++++++++++++++ selfdrive/ui/sunnypilot/qt/onroad/model.h | 7 +++ system/manager/manager.py | 1 + 6 files changed, 123 insertions(+) diff --git a/common/params_keys.h b/common/params_keys.h index a74f6061d0..c87894a959 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -171,6 +171,7 @@ inline static std::unordered_map keys = { {"HyundaiLongitudinalTuning", PERSISTENT}, {"DynamicExperimentalControl", PERSISTENT}, + {"BlindSpot", PERSISTENT | BACKUP}, // model panel params {"LagdToggle", PERSISTENT | BACKUP}, diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc index cf9729be18..0d42bd74f4 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc @@ -8,5 +8,63 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h" VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) { + param_watcher = new ParamWatcher(this); + connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString ¶m_name, const QString ¶m_value) { + paramsRefresh(); + }); + + main_layout = new QStackedLayout(this); + ListWidgetSP *list = new ListWidgetSP(this, false); + sunnypilotScreen = new QWidget(this); + QVBoxLayout* vlayout = new QVBoxLayout(sunnypilotScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + + std::vector > toggle_defs{ + { + "BlindSpot", + tr("Show Blind Spot Warnings"), + tr("Enabling this will display warnings when a vehicle is detected in your blind spot as long as your car has BSM supported."), + "../assets/offroad/icon_monitoring.png", + false, + }, + }; + + for (auto &[param, title, desc, icon, needs_restart] : toggle_defs) { + auto toggle = new ParamControlSP(param, title, desc, icon, this); + + bool locked = params.getBool((param + "Lock").toStdString()); + toggle->setEnabled(!locked); + + if (needs_restart && !locked) { + toggle->setDescription(toggle->getDescription() + tr(" Changing this setting will restart openpilot if the car is powered on.")); + + QObject::connect(uiState(), &UIState::engagedChanged, [toggle](bool engaged) { + toggle->setEnabled(!engaged); + }); + + QObject::connect(toggle, &ParamControlSP::toggleFlipped, [=](bool state) { + params.putBool("OnroadCycleRequested", true); + }); + } + + list->addItem(toggle); + toggles[param.toStdString()] = toggle; + param_watcher->addParam(param); + } + + sunnypilotScroller = new ScrollViewSP(list, this); + vlayout->addWidget(sunnypilotScroller); + + main_layout->addWidget(sunnypilotScreen); +} + +void VisualsPanel::paramsRefresh() { + if (!isVisible()) { + return; + } + + for (auto toggle : toggles) { + toggle.second->refresh(); + } } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h index 9f58104c4e..42e0688957 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h @@ -8,6 +8,9 @@ #pragma once #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +class ScrollViewSP; class VisualsPanel : public QWidget { Q_OBJECT @@ -15,4 +18,13 @@ class VisualsPanel : public QWidget { public: explicit VisualsPanel(QWidget *parent = nullptr); + void paramsRefresh(); + +protected: + QStackedLayout* main_layout = nullptr; + QWidget* sunnypilotScreen = nullptr; + ScrollViewSP *sunnypilotScroller = nullptr; + Params params; + std::map toggles; + ParamWatcher * param_watcher; }; diff --git a/selfdrive/ui/sunnypilot/qt/onroad/model.cc b/selfdrive/ui/sunnypilot/qt/onroad/model.cc index 617b64f58e..af0177c344 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/model.cc +++ b/selfdrive/ui/sunnypilot/qt/onroad/model.cc @@ -6,3 +6,47 @@ */ #include "selfdrive/ui/sunnypilot/qt/onroad/model.h" + + +void ModelRendererSP::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) { + ModelRenderer::update_model(model, lead); + const auto &model_position = model.getPosition(); + const auto &lane_lines = model.getLaneLines(); + float max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); + int max_idx = get_path_length_idx(lane_lines[0], max_distance); + // update blindspot vertices + float max_distance_barrier = 100; + int max_idx_barrier = std::min(max_idx, get_path_length_idx(lane_lines[0], max_distance_barrier)); + mapLineToPolygon(model.getLaneLines()[1], 0.2, -0.05, &left_blindspot_vertices, max_idx_barrier); + mapLineToPolygon(model.getLaneLines()[2], 0.2, -0.05, &right_blindspot_vertices, max_idx_barrier); +} + +void ModelRendererSP::drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &surface_rect) { + auto *s = uiState(); + auto &sm = *(s->sm); + bool blindspot = Params().getBool("BlindSpot"); + + if (blindspot) { + bool left_blindspot = sm["carState"].getCarState().getLeftBlindspot(); + bool right_blindspot = sm["carState"].getCarState().getRightBlindspot(); + + //painter.setBrush(QColor::fromRgbF(1.0, 0.0, 0.0, 0.4)); // Red with alpha for blind spot + + if (left_blindspot && !left_blindspot_vertices.isEmpty()) { + QLinearGradient gradient(0, 0, surface_rect.width(), 0); // Horizontal gradient from left to right + gradient.setColorAt(0.0, QColor(255, 165, 0, 102)); // Orange with alpha + gradient.setColorAt(1.0, QColor(255, 255, 0, 102)); // Yellow with alpha + painter.setBrush(gradient); + painter.drawPolygon(left_blindspot_vertices); + } + + if (right_blindspot && !right_blindspot_vertices.isEmpty()) { + QLinearGradient gradient(surface_rect.width(), 0, 0, 0); // Horizontal gradient from right to left + gradient.setColorAt(0.0, QColor(255, 165, 0, 102)); // Orange with alpha + gradient.setColorAt(1.0, QColor(255, 255, 0, 102)); // Yellow with alpha + painter.setBrush(gradient); + painter.drawPolygon(right_blindspot_vertices); + } + } + ModelRenderer::drawPath(painter, model, surface_rect.height()); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/model.h b/selfdrive/ui/sunnypilot/qt/onroad/model.h index 8569e58f66..24404f32f0 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/model.h +++ b/selfdrive/ui/sunnypilot/qt/onroad/model.h @@ -12,4 +12,11 @@ class ModelRendererSP : public ModelRenderer { public: ModelRendererSP() = default; + +private: + void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) override; + void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, const QRect &rect) override; + + QPolygonF left_blindspot_vertices; + QPolygonF right_blindspot_vertices; }; diff --git a/system/manager/manager.py b/system/manager/manager.py index 1d72beeda1..9afe1a4f26 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -44,6 +44,7 @@ def manager_init() -> None: sunnypilot_default_params: list[tuple[str, str | bytes]] = [ ("AutoLaneChangeTimer", "0"), ("AutoLaneChangeBsmDelay", "0"), + ("BlindSpot", "0"), ("BlinkerMinLateralControlSpeed", "20"), # MPH or km/h ("BlinkerPauseLateralControl", "0"), ("DynamicExperimentalControl", "0"), From 489d9164f8c8aba20b898c55e29e1c98f4598d09 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 6 Jun 2025 16:50:10 -0400 Subject: [PATCH 466/477] ui: model fuzzy search (#958) * models panel * models panel * fix ui report * stupid scroll * move model selector to models panel * cleanup whitespaces * cleanup bad merge * model search * support searching with short name --------- Co-authored-by: DevTekVE --- .../qt/offroad/settings/models_panel.cc | 33 ++++++++++++++++--- .../qt/offroad/settings/models_panel.h | 3 +- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 3c8d034c5a..1ce1b1b991 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -24,7 +24,17 @@ ModelsPanel::ModelsPanel(QWidget *parent) : QWidget(parent) { currentModelLblBtn = new ButtonControlSP(tr("Current Model"), tr("SELECT"), current_model); currentModelLblBtn->setValue(current_model); - connect(currentModelLblBtn, &ButtonControlSP::clicked, this, &ModelsPanel::handleCurrentModelLblBtnClicked); + connect(currentModelLblBtn, &ButtonControlSP::clicked, [=]() { + + InputDialog d(tr("Search Model"), this, tr("Enter search keywords, or leave blank to list all models."), false); + d.setMinLength(0); + const int ret = d.exec(); + if (ret) { + handleCurrentModelLblBtnClicked(d.text()); + } + + }); + connect(uiState(), &UIState::offroadTransition, [=](bool offroad) { is_onroad = !offroad; updateLabels(); @@ -134,7 +144,7 @@ void ModelsPanel::updateModelManagerState() { * @brief Handles the model bundle selection button click * Displays available bundles, allows selection, and initiates download */ -void ModelsPanel::handleCurrentModelLblBtnClicked() { +void ModelsPanel::handleCurrentModelLblBtnClicked(const QString &query) { currentModelLblBtn->setEnabled(false); currentModelLblBtn->setValue(tr("Fetching models...")); @@ -142,7 +152,8 @@ void ModelsPanel::handleCurrentModelLblBtnClicked() { QMap index_to_bundle; const auto bundles = model_manager.getAvailableBundles(); for (const auto &bundle: bundles) { - index_to_bundle.insert(bundle.getIndex(), QString::fromStdString(bundle.getDisplayName())); + std::string bundleStr = std::string(bundle.getDisplayName()) + " $SNAME$ " + std::string(bundle.getInternalName()); + index_to_bundle.insert(bundle.getIndex(), QString::fromStdString(bundleStr)); } // Sort bundles by index in descending order @@ -156,10 +167,24 @@ void ModelsPanel::handleCurrentModelLblBtnClicked() { bundleNames.append(index_to_bundle[index]); } + QStringList filteredBundleNames = searchFromList(query, bundleNames); + currentModelLblBtn->setValue(GetActiveModelName()); + if (filteredBundleNames.isEmpty()) { + ConfirmationDialog::alert(tr("No model found for keywords: %1").arg(query), this); + return; + } + + for (const QString &bundleName: filteredBundleNames) { + int index = bundleName.indexOf("$SNAME$"); + if (index != -1) { + filteredBundleNames.replace(filteredBundleNames.indexOf(bundleName), bundleName.left(index).trimmed()); + } + } + const QString selectedBundleName = MultiOptionDialog::getSelection( - tr("Select a Model"), bundleNames, GetActiveModelName(), this); + tr("Select a Model"), filteredBundleNames, GetActiveModelName(), this); if (selectedBundleName.isEmpty() || !canContinueOnMeteredDialog()) { return; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h index 4ba526651e..ee6a74a666 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h @@ -7,6 +7,7 @@ #pragma once +#include "selfdrive/ui/sunnypilot/qt/util.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" class ModelsPanel : public QWidget { @@ -30,7 +31,7 @@ private: // UI update related methods void updateLabels(); - void handleCurrentModelLblBtnClicked(); + void handleCurrentModelLblBtnClicked(const QString &query); void handleBundleDownloadProgress(); void showResetParamsDialog(); cereal::ModelManagerSP::Reader model_manager; From 5be2cd9b61a8fca813f2a17d3601791ff9dfd3e3 Mon Sep 17 00:00:00 2001 From: Nayan Date: Fri, 6 Jun 2025 17:06:13 -0400 Subject: [PATCH 467/477] bug: fix default model selection ui (#977) fix default model selection ui Co-authored-by: DevTekVE --- selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc index 1ce1b1b991..2d7367de1c 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.cc @@ -159,7 +159,7 @@ void ModelsPanel::handleCurrentModelLblBtnClicked(const QString &query) { // Sort bundles by index in descending order QStringList bundleNames; // Add "Default" as the first option - bundleNames.append(tr("Use Default")); + bundleNames.append(DEFAULT_MODEL); auto indices = index_to_bundle.keys(); std::sort(indices.begin(), indices.end(), std::greater()); @@ -191,7 +191,7 @@ void ModelsPanel::handleCurrentModelLblBtnClicked(const QString &query) { } // Handle "Stock" selection differently - if (selectedBundleName == tr("Use Default")) { + if (selectedBundleName == DEFAULT_MODEL) { params.remove("ModelManager_ActiveBundle"); currentModelLblBtn->setValue(tr("Default")); showResetParamsDialog(); From ab6d19271456c9a74e4fe5622ee64a8fabed3cc9 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Sat, 7 Jun 2025 03:47:09 -0400 Subject: [PATCH 468/477] `mapd` (#989) * init * some fixes * move * more * old navd helpers * bring back cereal * fix linting * more * add to cereal first * sp events * lint * implement in long plan * fixme-sp * refactor state machine * wrong state * start refactor controller * some type hints * init these * enable debug print * ui? ui! * print them out * fix spinner import * fix path * let's use gps chips directly for now * service missing * publish events * no nav for now * need to sub * no car state speed yet * missed event * Car: `CarStateSP` * fix tests * bring back car state speed limit * fix * use old controller for now * fix * fix source * type hints * none for now * formatting * more * create directory if does not exist * mypy my bt * policy param catch exceptions * handle all params with exceptions * more * single method * define types in init * rename * simpler op enabled check * more mypy stuff * rename * no need for brake pressed * don't reset if gas pressed * type hint all * type hint all * back to upstream * in another pr * no longer need data type * qlog * slc in another pr * use horizontal accuracy * set core affinity for all realtime processes * unused * sort * unused * type hint and slight cleanup * from old implementation * use directly * combine pm * slight more cleanup * type hints * even more type hint * lint * more cleanup * even less * license --- cereal/custom.capnp | 8 +- cereal/log.capnp | 2 +- cereal/services.py | 1 + common/params_keys.h | 20 ++ selfdrive/selfdrived/alerts_offroad.json | 4 + selfdrive/ui/sunnypilot/SConscript | 7 +- .../ui/sunnypilot/qt/common/json_fetcher.h | 41 +++ .../offroad/settings/osm/locations_fetcher.h | 63 ++++ .../qt/offroad/settings/osm/models_fetcher.cc | 158 ++++++++++ .../qt/offroad/settings/osm/models_fetcher.h | 143 +++++++++ .../qt/offroad/settings/osm_panel.cc | 283 ++++++++++++++++++ .../qt/offroad/settings/osm_panel.h | 232 ++++++++++++++ .../qt/offroad/settings/settings.cc | 2 + sunnypilot/mapd/__init__.py | 0 sunnypilot/mapd/live_map_data/__init__.py | 22 ++ .../mapd/live_map_data/base_map_data.py | 76 +++++ sunnypilot/mapd/live_map_data/debug.py | 57 ++++ sunnypilot/mapd/live_map_data/osm_map_data.py | 51 ++++ sunnypilot/mapd/live_map_data/standalone.py | 42 +++ sunnypilot/mapd/mapd_installer.py | 152 ++++++++++ sunnypilot/mapd/mapd_manager.py | 146 +++++++++ sunnypilot/navd/helpers.py | 189 ++++++++++++ .../selfdrive/assets/offroad/icon_map.png | 3 + system/hardware/hw.py | 8 + system/manager/manager.py | 3 + system/manager/process_config.py | 7 + third_party/mapd_pfeiferj/README.md | 2 + third_party/mapd_pfeiferj/mapd | Bin 0 -> 8192000 bytes 28 files changed, 1719 insertions(+), 3 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/common/json_fetcher.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.cc create mode 100644 selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.h create mode 100644 sunnypilot/mapd/__init__.py create mode 100644 sunnypilot/mapd/live_map_data/__init__.py create mode 100644 sunnypilot/mapd/live_map_data/base_map_data.py create mode 100644 sunnypilot/mapd/live_map_data/debug.py create mode 100644 sunnypilot/mapd/live_map_data/osm_map_data.py create mode 100644 sunnypilot/mapd/live_map_data/standalone.py create mode 100755 sunnypilot/mapd/mapd_installer.py create mode 100644 sunnypilot/mapd/mapd_manager.py create mode 100644 sunnypilot/navd/helpers.py create mode 100644 sunnypilot/selfdrive/assets/offroad/icon_map.png create mode 100644 third_party/mapd_pfeiferj/README.md create mode 100755 third_party/mapd_pfeiferj/mapd diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 8a42f8b6a5..c9e4f3e16d 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -226,7 +226,13 @@ struct BackupManagerSP @0xf98d843bfd7004a3 { struct CarStateSP @0xb86e6369214c01c8 { } -struct CustomReserved8 @0xf416ec09499d9d19 { +struct LiveMapDataSP @0xf416ec09499d9d19 { + speedLimitValid @0 :Bool; + speedLimit @1 :Float32; + speedLimitAheadValid @2 :Bool; + speedLimitAhead @3 :Float32; + speedLimitAheadDistance @4 :Float32; + roadName @5 :Text; } struct CustomReserved9 @0xa1680744031fdb2d { diff --git a/cereal/log.capnp b/cereal/log.capnp index 412660bc6a..46d1d25d64 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -2610,7 +2610,7 @@ struct Event { carControlSP @112 :Custom.CarControlSP; backupManagerSP @113 :Custom.BackupManagerSP; carStateSP @114 :Custom.CarStateSP; - customReserved8 @115 :Custom.CustomReserved8; + liveMapDataSP @115 :Custom.LiveMapDataSP; customReserved9 @116 :Custom.CustomReserved9; customReserved10 @136 :Custom.CustomReserved10; customReserved11 @137 :Custom.CustomReserved11; diff --git a/cereal/services.py b/cereal/services.py index 4097e484f6..3e72041b72 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -84,6 +84,7 @@ _services: dict[str, tuple] = { "carParamsSP": (True, 0.02, 1), "carControlSP": (True, 100., 10), "carStateSP": (True, 100., 10), + "liveMapDataSP": (True, 1., 1), # debug "uiDebug": (True, 0., 1), diff --git a/common/params_keys.h b/common/params_keys.h index c87894a959..d2863d7b33 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -175,4 +175,24 @@ inline static std::unordered_map keys = { // model panel params {"LagdToggle", PERSISTENT | BACKUP}, + + // mapd + {"MapAdvisorySpeedLimit", CLEAR_ON_ONROAD_TRANSITION}, + {"MapdVersion", PERSISTENT}, + {"MapSpeedLimit", CLEAR_ON_ONROAD_TRANSITION}, + {"NextMapSpeedLimit", CLEAR_ON_ONROAD_TRANSITION}, + {"Offroad_OSMUpdateRequired", CLEAR_ON_MANAGER_START}, + {"OsmDbUpdatesCheck", CLEAR_ON_MANAGER_START}, // mapd database update happens with device ON, reset on boot + {"OSMDownloadBounds", PERSISTENT}, + {"OsmDownloadedDate", PERSISTENT}, + {"OSMDownloadLocations", PERSISTENT}, + {"OSMDownloadProgress", CLEAR_ON_MANAGER_START}, + {"OsmLocal", PERSISTENT}, + {"OsmLocationName", PERSISTENT}, + {"OsmLocationTitle", PERSISTENT}, + {"OsmLocationUrl", PERSISTENT}, + {"OsmStateName", PERSISTENT}, + {"OsmStateTitle", PERSISTENT}, + {"OsmWayTest", PERSISTENT}, + {"RoadName", CLEAR_ON_ONROAD_TRANSITION}, }; diff --git a/selfdrive/selfdrived/alerts_offroad.json b/selfdrive/selfdrived/alerts_offroad.json index 68f5949398..6ea1bcf233 100644 --- a/selfdrive/selfdrived/alerts_offroad.json +++ b/selfdrive/selfdrived/alerts_offroad.json @@ -48,5 +48,9 @@ "OffroadMode_Status": { "text": "sunnypilot is now in Always Offroad mode. sunnypilot won't start until Always Offroad mode is disabled. Go to \"Settings\" -> \"Device\" to exit Always Offroad mode.", "severity": 1 + }, + "Offroad_OSMUpdateRequired": { + "text": "OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display.\n\n%1", + "severity": 0 } } diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 9a9e395d92..de7f3efbc5 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -26,6 +26,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/max_time_offroad.cc", "sunnypilot/qt/offroad/settings/brightness.cc", "sunnypilot/qt/offroad/settings/models_panel.cc", + "sunnypilot/qt/offroad/settings/osm_panel.cc", "sunnypilot/qt/offroad/settings/settings.cc", "sunnypilot/qt/offroad/settings/software_panel.cc", "sunnypilot/qt/offroad/settings/sunnylink_panel.cc", @@ -54,6 +55,10 @@ network_src = [ "sunnypilot/qt/network/sunnylink/services/user_service.cc", ] +osm_panel_qt_src = [ + "sunnypilot/qt/offroad/settings/osm/models_fetcher.cc", +] + vehicle_panel_qt_src = [ "sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc", "sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc", @@ -76,7 +81,7 @@ brand_settings_qt_src = [ ] sp_widgets_src = widgets_src + network_src -sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src + brand_settings_qt_src +sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src + brand_settings_qt_src + osm_panel_qt_src sp_qt_util = qt_util Export('sp_widgets_src', 'sp_qt_src', "sp_qt_util") diff --git a/selfdrive/ui/sunnypilot/qt/common/json_fetcher.h b/selfdrive/ui/sunnypilot/qt/common/json_fetcher.h new file mode 100644 index 0000000000..5691f600a6 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/common/json_fetcher.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include +#include +#include +#include + +class JsonFetcher { +public: + static QJsonObject getJsonFromURL(const QString &url) { + const auto qurl = QUrl(url); + QNetworkAccessManager manager; + const QNetworkRequest request(qurl); + QNetworkReply *reply = manager.get(request); + QEventLoop loop; + + // Send GET request + + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + if (reply->error() != QNetworkReply::NoError) { + qWarning() << "Failed to fetch data from URL: " << reply->errorString(); + return QJsonObject(); + } + + const QByteArray responseData = reply->readAll(); + const QJsonDocument doc = QJsonDocument::fromJson(responseData); + QJsonObject json = doc.object(); + + reply->deleteLater(); + return json; + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h new file mode 100644 index 0000000000..32d169ad15 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include // for std::sort +#include +#include +#include + +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/common/json_fetcher.h" + +static const std::tuple defaultLocation = std::make_tuple("== None ==", ""); + +// New class LocationsFetcher that handles web requests and JSON parsing +class LocationsFetcher { +public: + inline std::vector > + getLocationsFromURL(const QUrl &url, const std::tuple &customLocation = defaultLocation) const { + // Initialize an empty vector to hold the locations + std::vector > locations; + + JsonFetcher fetcher; + QJsonObject json = fetcher.getJsonFromURL(url.toString()); + + for (auto it = json.begin(); it != json.end(); ++it) { + QString code = it.key(); + QJsonObject obj = it.value().toObject(); + QString fullName = obj["full_name"].toString(); + + locations.push_back(std::make_tuple(fullName, code, QString(), QString())); + } + // Sort locations by full name + std::sort(locations.begin(), locations.end(), [](const auto &lhs, const auto &rhs) { + return std::get<0>(lhs) < std::get<0>(rhs); // Compare full names + }); + // Optionally, you can now add defaultName entry at the beginning + locations.insert(locations.begin(), std::tuple_cat(customLocation, std::make_tuple("", ""))); + return locations; + } + + inline std::vector > + getLocationsFromURL(const QString &url, const std::tuple &customLocation = defaultLocation) const { + return getLocationsFromURL(QUrl(url), customLocation); + } + + inline std::vector > + getOsmLocations(const std::tuple &customLocation = defaultLocation) const { + return getLocationsFromURL( "https://raw.githubusercontent.com/pfeiferj/openpilot-mapd/main/nation_bounding_boxes.json", customLocation); + } + + inline std::vector > + getUsStatesLocations(const std::tuple &customLocation = defaultLocation) const { + return getLocationsFromURL( "https://raw.githubusercontent.com/pfeiferj/openpilot-mapd/main/us_states_bounding_boxes.json", customLocation); + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc new file mode 100644 index 0000000000..b67e41e09d --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.cc @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h" + +#include + +ModelsFetcher::ModelsFetcher(QObject *parent) : QObject(parent) { + manager = new QNetworkAccessManager(this); +} + +QByteArray ModelsFetcher::verifyFileHash(const QString &filePath, const QString &expectedHash, bool &hashMatches) { + hashMatches = false; // Default to false + QByteArray fileData; + + if (expectedHash.isEmpty()) { + // If no hash is provided, assume verification isn't required but return the file data + hashMatches = true; + } else { + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + QCryptographicHash hash(QCryptographicHash::Sha256); // Or your chosen algorithm + fileData = file.readAll(); // Read the file data once + hash.addData(fileData); + file.close(); + + QString currentHash = QString(hash.result().toHex()); + hashMatches = (currentHash == expectedHash); + } + } + + // Return the file data if hash matches or no hash was provided; empty otherwise + return hashMatches ? fileData : QByteArray(); +} + + +void ModelsFetcher::download(const DownloadInfo &downloadInfo, const QString &filename, const QString &destinationPath) { + QString fullPath = destinationPath + "/" + filename; + QFileInfo fileInfo(fullPath); + bool hashMatches = false; + QByteArray data = verifyFileHash(fullPath, downloadInfo.sha256, hashMatches); + + if (fileInfo.exists() && hashMatches) { + // Hash matches or no hash provided, and we have the file data + LOGD("File already downloaded and verified: %s", filename.toStdString().c_str()); + emit downloadProgress(100); + emit downloadComplete(data, true); // Use the data returned from verifyFileHash + return; // Exit early + } + + // Proceed with download if file does not exist or hash verification failed + QNetworkRequest request(downloadInfo.url); + QNetworkReply *reply = manager->get(request); + connect(reply, &QNetworkReply::downloadProgress, this, &ModelsFetcher::onDownloadProgress); + connect(reply, &QNetworkReply::finished, this, [this, reply, destinationPath, filename, downloadInfo]() { + onFinished(reply, destinationPath, filename, downloadInfo.sha256); + }); +} + +QString extractFileName(const QString &contentDisposition) { + const QString filenameTag = "filename="; + const int idx = contentDisposition.indexOf(filenameTag); + if (idx < 0) { + return QString(); + } + + QString filename = contentDisposition.mid(idx + filenameTag.length()); + if (filename.startsWith("\"") && filename.endsWith("\"")) { + return filename.mid(1, filename.size() - 2); + } + + return filename; +} + +void ModelsFetcher::onFinished(QNetworkReply *reply, const QString &destinationPath, const QString &filename, const QString &expectedHash) { + // Handle download error + if (reply->error()) { + return; // Possibly emit a signal or log an error as per your error handling policy + } + + const QByteArray data = reply->readAll(); + + QString finalFilename = filename; + if (finalFilename.isEmpty()) { + finalFilename = extractFileName(reply->header(QNetworkRequest::ContentDispositionHeader).toString()); + } + + QString finalPath = QDir(destinationPath).filePath(finalFilename); + + // Save the downloaded file + QFile file(finalPath); + //ensure if the path exists and if not create it + if (!QDir().mkpath(destinationPath)) { + LOGE("Unable to create directory: %s", destinationPath.toStdString().c_str()); + emit downloadFailed(filename); + return; // Stop further processing + } + + //Retry the file open and write 3 times with a little delay between each retry + for (int i = 0; i < 3; i++) { + if (file.isOpen()) break; + + file.open(QIODevice::WriteOnly); + if (!file.isOpen()) QThread::msleep(100); + } + + // If the file is still not open, log an error and emit a failure signal + if (!file.isOpen()) { + LOGE("Unable to open file for writing: %s", finalPath.toStdString().c_str()); + emit downloadFailed(filename); + return; // Stop further processing + } + + file.write(data); + file.close(); + + bool hashMatches = false; + verifyFileHash(finalPath, expectedHash, hashMatches); + + // Verify the file hash if expectedHash is provided + if (!expectedHash.isEmpty() && !hashMatches) { + LOGE("The downloaded file didn't pass the hash validation!: %s", filename.toStdString().c_str()); + // Hash verification failed, handle accordingly + // This could involve deleting the file, logging an error, or emitting a failure signal + QFile::remove(finalPath); // Example action: Remove the invalid file + emit downloadFailed(filename); + return; // Stop further processing + } + + emit downloadComplete(data, false); // Emit your success signal +} + +void ModelsFetcher::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + const double progress = (bytesReceived * 100.0) / bytesTotal; + emit downloadProgress(progress); +} + +std::vector ModelsFetcher::getModelsFromURL(const QUrl &url) { + std::vector models; + JsonFetcher fetcher; + QJsonObject json = fetcher.getJsonFromURL(url.toString()); + for (auto it = json.begin(); it != json.end(); ++it) { + models.push_back(Model(it.value().toObject())); + } + return models; +} + +std::vector ModelsFetcher::getModelsFromURL(const QString &url) { + return getModelsFromURL(QUrl(url)); +} + +std::vector ModelsFetcher::getModelsFromURL() { + return getModelsFromURL("https://docs.sunnypilot.ai/models_v5.json"); +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h new file mode 100644 index 0000000000..73890e9d5a --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm/models_fetcher.h @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include // for std::sort +#include +#include +#include + +#include +#include +#include + +#include "common/swaglog.h" +#include "common/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/qt/common/json_fetcher.h" +#ifdef SUNNYPILOT +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#else +#include "selfdrive/ui/qt/widgets/controls.h" +#endif +#include "system/hardware/hw.h" + +static const QString MODELS_PATH = Hardware::PC() ? QDir::homePath() + "/.comma/media/0/models/" : "/data/media/0/models/"; + +struct DownloadInfo { + QString url; + QString sha256; +}; + +// New class ModelsFetcher with a new function that handles web requests and JSON parsing for the new JSON structure +class Model { +public: + explicit Model(const QJsonObject &json) { + displayName = json["display_name"].toString(); + fullName = json["full_name"].toString(); + fileName = json["file_name"].toString(); + + // Parse downloadUri as an object + QJsonObject downloadUriObj = json["download_uri"].toObject(); + downloadUri.url = downloadUriObj["url"].toString(); + downloadUri.sha256 = downloadUriObj["sha256"].toString(); + + fullNameNav = json["full_name_nav"].toString(); + fileNameNav = json["file_name_nav"].toString(); + + // Parse downloadUriNav as an object + QJsonObject downloadUriNavObj = json["download_uri_nav"].toObject(); + downloadUriNav.url = downloadUriNavObj["url"].toString(); + downloadUriNav.sha256 = downloadUriNavObj["sha256"].toString(); + + fullNameMetadata = json["full_name_metadata"].toString(); + fileNameMetadata = json["file_name_metadata"].toString(); + + // Parse downloadUriMetadata as an object + QJsonObject downloadUriMetadataObj = json["download_uri_metadata"].toObject(); + downloadUriMetadata.url = downloadUriMetadataObj["url"].toString(); + downloadUriMetadata.sha256 = downloadUriMetadataObj["sha256"].toString(); + + index = json["index"].toString(); + environment = json["environment"].toString(); + generation = json["generation"].toString(); + } + + // Method to convert model back to QJsonObject, if needed + QJsonObject toJson() const { + QJsonObject json; + json["display_name"] = displayName; + json["full_name"] = fullName; + json["file_name"] = fileName; + + QJsonObject uriObj; + uriObj["url"] = downloadUri.url; + uriObj["sha256"] = downloadUri.sha256; + json["download_uri"] = uriObj; + + QJsonObject uriNavObj; + uriNavObj["url"] = downloadUriNav.url; + uriNavObj["sha256"] = downloadUriNav.sha256; + json["download_uri_nav"] = uriNavObj; + + QJsonObject uriMetadataObj; + uriMetadataObj["url"] = downloadUriMetadata.url; + uriMetadataObj["sha256"] = downloadUriMetadata.sha256; + json["download_uri_metadata"] = uriMetadataObj; + + json["full_name_nav"] = fullNameNav; + json["file_name_nav"] = fileNameNav; + json["full_name_metadata"] = fullNameMetadata; + json["file_name_metadata"] = fileNameMetadata; + json["index"] = index; + json["environment"] = environment; + json["generation"] = generation; + return json; + } + + QString displayName; + QString fullName; + QString fileName; + DownloadInfo downloadUri; + DownloadInfo downloadUriNav; + DownloadInfo downloadUriMetadata; + + QString fullNameNav; + QString fileNameNav; + QString fullNameMetadata; + QString fileNameMetadata; + QString index; + QString environment; + QString generation; +}; + +class ModelsFetcher : public QObject { + Q_OBJECT + +public: + explicit ModelsFetcher(QObject *parent = nullptr); + void download(const DownloadInfo &url, const QString &filename = "", const QString &destinationPath = MODELS_PATH); + static std::vector getModelsFromURL(const QUrl &url); + static std::vector getModelsFromURL(const QString &url); + static std::vector getModelsFromURL(); + +signals: + void downloadProgress(double percentage); + void downloadComplete(const QByteArray &data, bool fromCache = false); + void downloadFailed(const QString &filename); + +private: + // static bool verifyFileHash(const QString& filePath, const QString& expectedHash); + static QByteArray verifyFileHash(const QString &filePath, const QString &expectedHash, bool &hashMatches); + void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void onFinished(QNetworkReply *reply, const QString &destinationPath, const QString &filename, + const QString &expectedHash); + + QNetworkAccessManager *manager; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.cc new file mode 100644 index 0000000000..332fd71a18 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.cc @@ -0,0 +1,283 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.h" + +#include +#include +#include + +#include "common/swaglog.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +OsmPanel::OsmPanel(QWidget *parent) : QFrame(parent) { + main_layout = new QStackedLayout(this); + + const auto list = new ListWidgetSP(this, false); + list->addItem(mapdVersion = new LabelControlSP(tr("Mapd Version"), "Loading...")); + list->addItem(setupOsmDeleteMapsButton(parent)); + list->addItem(offlineMapsETA = new LabelControlSP(tr("Offline Maps ETA"), "")); + list->addItem(offlineMapsElapsed = new LabelControlSP(tr("Time Elapsed"), "")); + list->addItem(setupOsmUpdateButton(parent)); + list->addItem(setupOsmDownloadButton(parent)); + list->addItem(setupUsStatesButton(parent)); + + connect(uiStateSP(), &UIStateSP::offroadTransition, [=](bool offroad) { + updateLabels(); + }); + + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, QOverload<>::of(&OsmPanel::updateLabels)); + timer->start(FAST_REFRESH_INTERVAL); // Time specified in milliseconds. + updateLabels(); + + osmScreen = new QWidget(this); + auto *vlayout = new QVBoxLayout(osmScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + vlayout->addWidget(new ScrollViewSP(list, this), 1); + main_layout->addWidget(osmScreen); +} + +ButtonControlSP *OsmPanel::setupOsmDeleteMapsButton(QWidget *parent) { + osmDeleteMapsBtn = new ButtonControlSP(tr("Downloaded Maps"), tr("DELETE")); // Updated on updateLabels() + connect(osmDeleteMapsBtn, &ButtonControlSP::clicked, [=]() { + if (showConfirmationDialog(parent, tr("This will delete ALL downloaded maps\n\nAre you sure you want to delete all the maps?"), tr("Yes, delete all the maps."))) { + QtConcurrent::run([=]() { + QDir dir(MAP_PATH); + osmDeleteMapsBtn->setEnabled(false); + osmDeleteMapsBtn->setText("⌛"); + dir.removeRecursively(); + updateMapSize(); + osmDeleteMapsBtn->setEnabled(true); + osmDeleteMapsBtn->setText(tr("DELETE")); + }); + updateLabels(); + } + }); + return osmDeleteMapsBtn; +} + +ButtonControlSP *OsmPanel::setupOsmUpdateButton(QWidget *parent) { + osmUpdateBtn = new ButtonControlSP(tr("Database Update"), tr("CHECK")); // Updated on updateLabels() + connect(osmUpdateBtn, &ButtonControlSP::clicked, [=]() { + if (osm_download_in_progress && !download_failed_state) { + updateLabels(); + } else if (showConfirmationDialog(parent)) { + osm_download_in_progress = true; + params.putBool("OsmDbUpdatesCheck", true); + updateLabels(); + } + }); + return osmUpdateBtn; +} + +ButtonControlSP *OsmPanel::setupOsmDownloadButton(QWidget *parent) { + osmDownloadBtn = new ButtonControlSP(tr("Country"), tr("SELECT")); + connect(osmDownloadBtn, &ButtonControlSP::clicked, [=]() { + osmDownloadBtn->setEnabled(false); + osmDownloadBtn->setValue(tr("Fetching Country list...")); + const std::vector > locations = getOsmLocations(); + osmDownloadBtn->setEnabled(true); + osmDownloadBtn->setValue(""); + const QString initTitle = QString::fromStdString(params.get("OsmLocationTitle")); + const QString currentTitle = ((initTitle == "== None ==") || (initTitle.length() == 0)) ? "== None ==" : initTitle; + + QStringList locationTitles; + for (auto &loc: locations) { + locationTitles.push_back(std::get<0>(loc)); + } + + const QString selection = MultiOptionDialog::getSelection(tr("Country"), locationTitles, currentTitle, this); + if (!selection.isEmpty()) { + params.put("OsmLocal", "1"); + params.put("OsmLocationTitle", selection.toStdString()); + for (auto &loc: locations) { + if (std::get<0>(loc) == selection) { + params.put("OsmLocationName", std::get<1>(loc).toStdString()); + break; + } + } + if (params.get("OsmLocationName") == "US") { + usStatesBtn->click(); + return; + } else if (selection != "== None ==") { + if (showConfirmationDialog(parent)) { + osm_download_in_progress = true; + params.putBool("OsmDbUpdatesCheck", true); + updateLabels(); + } + } + } + updateLabels(); + }); + return osmDownloadBtn; +} + +ButtonControlSP *OsmPanel::setupUsStatesButton(QWidget *parent) { + usStatesBtn = new ButtonControlSP(tr("State"), tr("SELECT")); + connect(usStatesBtn, &ButtonControlSP::clicked, [=]() { + const std::tuple allStatesOption = std::make_tuple("All States (~4.8 GB)", "All"); + usStatesBtn->setEnabled(false); + usStatesBtn->setValue(tr("Fetching State list...")); + const std::vector > locations = + getUsStatesLocations(allStatesOption); + usStatesBtn->setEnabled(true); + usStatesBtn->setValue(""); + const QString initTitle = QString::fromStdString(params.get("OsmStateTitle")); + const QString currentTitle = ((initTitle == std::get<0>(allStatesOption)) || (initTitle.length() == 0)) ? tr("All") : initTitle; + + QStringList locationTitles; + for (auto &loc: locations) { + locationTitles.push_back(std::get<0>(loc)); + } + + const QString selection = MultiOptionDialog::getSelection(tr("State"), locationTitles, currentTitle, this); + if (!selection.isEmpty()) { + params.put("OsmStateTitle", selection.toStdString()); + for (auto &loc: locations) { + if (std::get<0>(loc) == selection) { + params.put("OsmStateName", std::get<1>(loc).toStdString()); + break; + } + } + usStatesBtn->setValue(selection); + if (showConfirmationDialog(parent)) { + osm_download_in_progress = true; + params.putBool("OsmDbUpdatesCheck", true); + updateLabels(); + } + } + updateLabels(); + }); + usStatesBtn->setVisible(false); // initially hidden + return usStatesBtn; +} + +void OsmPanel::showEvent(QShowEvent *event) { + updateLabels(); // For snappier feeling + if (!timer->isActive()) { + timer->start(FAST_REFRESH_INTERVAL); + } +} + +void OsmPanel::hideEvent(QHideEvent *event) { + if (timer->isActive()) { + timer->stop(); + } +} + + +void OsmPanel::updateLabels() { + if (!isVisible()) { + return; + } + mapd_version = params.get("MapdVersion"); + mapdVersion->setText(mapd_version.c_str()); + + updateMapSize(); + osm_download_locations = mem_params.get("OSMDownloadLocations"); + osm_download_in_progress = !osm_download_locations.empty(); + + timer->setInterval(osm_download_in_progress ? FAST_REFRESH_INTERVAL : SLOW_REFRESH_INTERVAL); + LOGT("Timer Interval %d", timer->interval()); + + const std::string osmLastDownloadTimeStr = params.get("OsmDownloadedDate"); + if (!lastDownloadedTimePoint.has_value() && !osmLastDownloadTimeStr.empty()) { + const double osmLastDownloadTime = std::stod(osmLastDownloadTimeStr); + lastDownloadedTimePoint = std::chrono::system_clock::from_time_t(static_cast(osmLastDownloadTime)); + } + + osmDownloadBtn->setEnabled(!osm_download_in_progress); + usStatesBtn->setEnabled(!osm_download_in_progress); + + updateDownloadProgress(); + + const QString locationName = QString::fromStdString(params.get("OsmLocationName")); + const bool isUs = !locationName.isEmpty() && locationName == "US"; + usStatesBtn->setVisible(isUs); + + if (!locationName.isEmpty()) { + if (!isUs) { + params.remove("OsmStateName"); + params.remove("OsmStateTitle"); + } + osmUpdateBtn->setVisible(true); + } else { + params.remove("OsmLocal"); + params.remove("OsmLocationName"); + params.remove("OsmLocationTitle"); + params.remove("OsmStateName"); + params.remove("OsmStateTitle"); + osmUpdateBtn->setVisible(false); + usStatesBtn->setVisible(false); + } + + osmDownloadBtn->setValue(QString::fromStdString(params.get("OsmLocationTitle"))); + usStatesBtn->setValue(QString::fromStdString(params.get("OsmStateTitle"))); + update(); +} + +void OsmPanel::updateDownloadProgress() { + const auto pending_update_check = params.getBool("OsmDbUpdatesCheck"); + const QJsonObject osmDownloadProgress = QJsonDocument::fromJson(params.get("OSMDownloadProgress").c_str()).object(); + if (osm_download_in_progress && lastDownloadedTimePoint.has_value()) { + offlineMapsETA->setVisible(true); + offlineMapsElapsed->setVisible(true); + offlineMapsETA->setText(calculateETA(osmDownloadProgress, lastDownloadedTimePoint.value())); + offlineMapsElapsed->setText(calculateElapsedTime(osmDownloadProgress, lastDownloadedTimePoint.value())); + } else { + offlineMapsETA->setVisible(false); + offlineMapsElapsed->setVisible(false); + } + + const int total_files = extractIntFromJson(osmDownloadProgress, "total_files"); + const int downloaded_files = extractIntFromJson(osmDownloadProgress, "downloaded_files"); + download_failed_state = total_files && osm_download_in_progress && !lastDownloadedTimePoint.has_value() && downloaded_files < total_files; + + QString updateButtonText = processUpdateStatus(pending_update_check, total_files, downloaded_files, osmDownloadProgress, download_failed_state); + + osmUpdateBtn->setValue(updateButtonText); + osmUpdateBtn->setText(osm_download_in_progress && !download_failed_state ? tr("REFRESH") : tr("UPDATE")); + osmDeleteMapsBtn->setValue(formatSize(mapsDirSize)); +} + +int OsmPanel::extractIntFromJson(const QJsonObject &json, const QString &key) { + return (json.contains(key)) ? json[key].toInt() : 0; +} + +QString OsmPanel::processUpdateStatus(bool pending_update, int total_files, int downloaded_files, const QJsonObject &json, bool failed_state) { + if (pending_update && !osm_download_in_progress && !total_files) { + lastDownloadedTimePoint.reset(); + return tr("Download starting..."); + } else if (failed_state) { + return tr("Error: Invalid download. Retry."); + } else if (osm_download_in_progress && total_files > downloaded_files) { + return formatDownloadStatus(json); + } else if (osm_download_in_progress && downloaded_files >= total_files) { + osm_download_in_progress = false; + lastDownloadedTimePoint.reset(); + return tr("Download complete!"); + } + + if (lastDownloadedTimePoint.has_value()) { + QDateTime dateTime = QDateTime::fromTime_t(std::chrono::system_clock::to_time_t(lastDownloadedTimePoint.value())); //fromMSecsSinceEpoch(duration); + dateTime = dateTime.toLocalTime(); + return QString("%1").arg(dateTime.toString("yyyy-MM-dd HH:mm:ss")); + } + + return ""; +} + +void OsmPanel::updateMapSize() { + if (mapSizeFuture.has_value() && mapSizeFuture.value().isFinished()) { + mapsDirSize = mapSizeFuture.value().result(); + } + + if (!mapSizeFuture.has_value() || !mapSizeFuture.value().isRunning()) { + mapSizeFuture = QtConcurrent::run(getDirSize, MAP_PATH); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.h new file mode 100644 index 0000000000..171129a11c --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.h @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "selfdrive/ui/qt/network/wifi_manager.h" +#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm/locations_fetcher.h" +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/sunnypilot/ui.h" +#include "system/hardware/hw.h" + +constexpr int FAST_REFRESH_INTERVAL = 1000; // ms +constexpr int SLOW_REFRESH_INTERVAL = 5000; // ms + +static const QString MAP_PATH = Hardware::PC() ? QDir::homePath() + "/.comma/media/0/osm/offline/" : "/data/media/0/osm/offline/"; + +class OsmPanel : public QFrame { + Q_OBJECT + +public: + explicit OsmPanel(QWidget *parent = nullptr); + +private: + QStackedLayout *main_layout = nullptr; + QWidget *osmScreen = nullptr; + Params params; + Params mem_params{Hardware::PC() ? "" : "/dev/shm/params"}; + std::map toggles; + std::optional > mapSizeFuture; + const SubMaster &sm = *uiStateSP()->sm; + + + bool is_onroad = false; + std::string mapd_version; + + bool isWifi() const { return sm["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI; } + bool isMetered() const { return sm["deviceState"].getDeviceState().getNetworkMetered(); } + bool osm_download_in_progress = false; + bool download_failed_state = false; + quint64 mapsDirSize = 0; + + QLabel *osmUpdateLbl; + ButtonControlSP *osmDownloadBtn; + ButtonControlSP *osmUpdateBtn; + ButtonControlSP *usStatesBtn; + ButtonControlSP *osmDeleteMapsBtn; + + ButtonControlSP *setupOsmDeleteMapsButton(QWidget *parent);; + ButtonControlSP *setupOsmUpdateButton(QWidget *parent); + ButtonControlSP *setupOsmDownloadButton(QWidget *parent); + ButtonControlSP *setupUsStatesButton(QWidget *parent); + + QTimer *timer; + std::string osm_download_locations; + // void updateButtonControlSP(ButtonControlSP *btnControl, QWidget *parent, const QString &initTitle, const QString &allStatesOption); + + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + void updateLabels(); + void updateDownloadProgress(); + static int extractIntFromJson(const QJsonObject &json, const QString &key); + QString processUpdateStatus(bool pending_update_check, int total_files, int downloaded_files, const QJsonObject &json, bool failed_state); + + ConfirmationDialog *confirmationDialog; + LabelControlSP *mapdVersion; + LabelControlSP *offlineMapsStatus; + LabelControlSP *offlineMapsETA; + LabelControlSP *offlineMapsElapsed; + std::optional lastDownloadedTimePoint; + LocationsFetcher locationsFetcher; + + void updateMapSize(); + + bool showConfirmationDialog(QWidget *parent, + const QString &message = QString(), + const QString &confirmButtonText = QString()) const { + const auto _is_metered = isMetered(); + const QString warning_message = _is_metered ? tr("\n\nWarning: You are on a metered connection!") : QString(); + QString final_message = message.isEmpty() ? tr("This will start the download process and it might take a while to complete.") : message; + final_message += warning_message; // Append the warning message if the connection is metered + + const QString final_buttonText = confirmButtonText.isEmpty() ? (_is_metered ? tr("Continue on Metered") : tr("Start Download")) : confirmButtonText; + + return ConfirmationDialog::confirm(final_message, final_buttonText, parent); + } + + // Refactored methods + std::vector > getOsmLocations(const std::tuple &customLocation = defaultLocation) const { + return locationsFetcher.getOsmLocations(customLocation); + } + + std::vector > getUsStatesLocations(const std::tuple &customLocation = defaultLocation) const { + return locationsFetcher.getUsStatesLocations(customLocation); + } + + static QString formatTime(const long timeInSeconds) { + const long minutes = timeInSeconds / 60; + const long seconds = timeInSeconds % 60; + + QString formattedTime; + if (minutes > 0) { + formattedTime = QString::number(minutes) + tr("m "); + } + formattedTime += QString::number(seconds) + tr("s"); + return formattedTime; + } + + static QString calculateElapsedTime(const QJsonObject &jsonData, const std::chrono::system_clock::time_point &startTime) { + using namespace std::chrono; + if (!jsonData.contains("total_files") || !jsonData.contains("downloaded_files")) + return tr("Calculating..."); + + const int totalFiles = jsonData["total_files"].toInt(); + const int downloadedFiles = jsonData["downloaded_files"].toInt(); + + if (downloadedFiles >= totalFiles || totalFiles <= 0) return tr("Downloaded"); + + const long elapsed = duration_cast(system_clock::now() - startTime).count(); + + if (elapsed == 0 || downloadedFiles == 0) return tr("Calculating..."); + + return formatTime(elapsed); + } + + static QString calculateETA(const QJsonObject &jsonData, const std::chrono::system_clock::time_point &startTime) { + using namespace std::chrono; + static steady_clock::time_point lastUpdateTime = steady_clock::now(); + static std::deque rateHistory; + + constexpr int minDataPoints = 3; + constexpr int historySize = 10; + + static QString lastETA = tr("Calculating ETA..."); + + if (duration_cast(steady_clock::now() - lastUpdateTime).count() < 1) { + return lastETA; + } + + if (!jsonData.contains("total_files") || !jsonData.contains("downloaded_files")) + return lastETA; + + const int totalFiles = jsonData["total_files"].toInt(); + const int downloadedFiles = jsonData["downloaded_files"].toInt(); + + if (totalFiles <= 0 || downloadedFiles >= totalFiles) { + return totalFiles <= 0 ? tr("Ready") : tr("Downloaded"); + } + + const long elapsed = duration_cast(system_clock::now() - startTime).count(); + if (elapsed == 0 || downloadedFiles == 0) return lastETA; + + const double rate = downloadedFiles / static_cast(elapsed); + if (rateHistory.size() >= historySize) rateHistory.pop_front(); + rateHistory.push_back(rate); + + if (rateHistory.size() < minDataPoints) return lastETA; + + double weightedSum = 0; + for (int i = 0, weight = 1; i < rateHistory.size(); ++i, ++weight) { + weightedSum += rateHistory[i] * weight; + } + const double avgRate = 2 * weightedSum / (rateHistory.size() * (rateHistory.size() + 1)); + + const long remainingTime = static_cast((totalFiles - downloadedFiles) / avgRate); + if (remainingTime <= 0) return lastETA; + + lastETA = tr("Time remaining: ") + formatTime(remainingTime); + lastUpdateTime = steady_clock::now(); + return lastETA; + } + + static QString formatDownloadStatus(const QJsonObject &json) { + if (!json.contains("total_files") || !json.contains("downloaded_files")) + return ""; + + const int total_files = json["total_files"].toInt(); + const int downloaded_files = json["downloaded_files"].toInt(); + + if (total_files <= 0) return tr("Ready"); + if (downloaded_files >= total_files) return tr("Downloaded"); + + const int percentage = static_cast(100.0 * downloaded_files / total_files); + return QString::asprintf("%d/%d (%d%%)", downloaded_files, total_files, percentage); + } + + QString formatSize(quint64 size) const { + if (size == 0 && (!mapSizeFuture.has_value() || mapSizeFuture.value().isRunning())) { + return tr("Calculating..."); + } + + constexpr qint64 kb = 1024; + constexpr qint64 mb = 1024 * kb; + constexpr qint64 gb = 1024 * mb; + + if (size < gb) { + const double sizeMB = size / static_cast(mb); + return QString::number(sizeMB, 'f', 2) + " MB"; + } else { + const double sizeGB = size / static_cast(gb); + return QString::number(sizeGB, 'f', 2) + " GB"; + } + } + + static quint64 getDirSize(QString dirPath) { + quint64 size = 0; + const QString actualDirPath = dirPath.startsWith("~") ? dirPath.replace(0, 1, QDir::homePath()) : dirPath; + QDirIterator it(actualDirPath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + if (it.fileInfo().isFile()) { + size += it.fileInfo().size(); + } + } + return size; + } +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc index e91de4c903..12d7bd74f7 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc @@ -18,6 +18,7 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h" +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/osm_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" #include "selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.h" @@ -85,6 +86,7 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) { PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"), PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/icons/speed_limit.png"), PanelInfo(" " + tr("Visuals"), new VisualsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_visuals.png"), + PanelInfo(" " + tr("OSM"), new OsmPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_map.png"), PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"), PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"), PanelInfo(" " + tr("Firehose"), new FirehosePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_firehose.svg"), diff --git a/sunnypilot/mapd/__init__.py b/sunnypilot/mapd/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sunnypilot/mapd/live_map_data/__init__.py b/sunnypilot/mapd/live_map_data/__init__.py new file mode 100644 index 0000000000..557ad06515 --- /dev/null +++ b/sunnypilot/mapd/live_map_data/__init__.py @@ -0,0 +1,22 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +from openpilot.common.swaglog import cloudlog + +LOOK_AHEAD_HORIZON_TIME = 15. # s. Time horizon for look ahead of turn speed sections to provide on liveMapDataSP msg. +_DEBUG = False +_CLOUDLOG_DEBUG = False +ROAD_NAME_TIMEOUT = 30 # secs +R = 6373000.0 # approximate radius of earth in mts +QUERY_RADIUS = 3000 # mts. Radius to use on OSM data queries. +QUERY_RADIUS_OFFLINE = 2250 # mts. Radius to use on offline OSM data queries. + + +def get_debug(msg, log_to_cloud=True): + if _CLOUDLOG_DEBUG and log_to_cloud: + cloudlog.debug(msg) + if _DEBUG: + print(msg) diff --git a/sunnypilot/mapd/live_map_data/base_map_data.py b/sunnypilot/mapd/live_map_data/base_map_data.py new file mode 100644 index 0000000000..536d7720b7 --- /dev/null +++ b/sunnypilot/mapd/live_map_data/base_map_data.py @@ -0,0 +1,76 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import time +from abc import abstractmethod, ABC + +from cereal import messaging +from openpilot.common.gps import get_gps_location_service +from openpilot.common.params import Params +from openpilot.sunnypilot.navd.helpers import Coordinate, coordinate_from_param + + +class BaseMapData(ABC): + def __init__(self): + self.params = Params() + + self.gps_location_service = get_gps_location_service(self.params) + self.sm = messaging.SubMaster(['livePose', 'carControl'] + [self.gps_location_service]) + self.pm = messaging.PubMaster(['liveMapDataSP']) + + self.last_position = coordinate_from_param("LastGPSPosition", self.params) + self.last_altitude = None + + @abstractmethod + def update_location(self) -> None: + pass + + @abstractmethod + def get_current_speed_limit(self) -> float: + pass + + @abstractmethod + def get_next_speed_limit_and_distance(self) -> tuple[float, float]: + pass + + @abstractmethod + def get_current_road_name(self) -> str: + pass + + def get_current_location(self) -> None: + gps = self.sm[self.gps_location_service] + + # ignore the message if the fix is invalid + gps_ok = self.sm.updated[self.gps_location_service] or (time.monotonic() - self.sm.logMonoTime[self.gps_location_service] / 1e9) > 2.0 + if not gps_ok and self.sm['livePose'].inputsOK: + return None + + # livePose has these data, but aren't on cereal + self.last_position = Coordinate(gps.latitude, gps.longitude) + self.last_altitude = gps.altitude + + def publish(self) -> None: + speed_limit = self.get_current_speed_limit() + next_speed_limit, next_speed_limit_distance = self.get_next_speed_limit_and_distance() + + mapd_sp_send = messaging.new_message('liveMapDataSP') + mapd_sp_send.valid = self.sm.all_checks(service_list=[self.gps_location_service, 'livePose']) + live_map_data = mapd_sp_send.liveMapDataSP + + live_map_data.speedLimitValid = bool(speed_limit > 0) + live_map_data.speedLimit = speed_limit + live_map_data.speedLimitAheadValid = bool(next_speed_limit > 0) + live_map_data.speedLimitAhead = next_speed_limit + live_map_data.speedLimitAheadDistance = next_speed_limit_distance + live_map_data.roadName = self.get_current_road_name() + + self.pm.send('liveMapDataSP', mapd_sp_send) + + def tick(self) -> None: + self.sm.update() + self.get_current_location() + self.update_location() + self.publish() diff --git a/sunnypilot/mapd/live_map_data/debug.py b/sunnypilot/mapd/live_map_data/debug.py new file mode 100644 index 0000000000..da9f4d7771 --- /dev/null +++ b/sunnypilot/mapd/live_map_data/debug.py @@ -0,0 +1,57 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +# DISCLAIMER: This code is intended principally for development and debugging purposes. +# Although it provides a standalone entry point to the program, users should refer +# to the actual implementations for consumption. Usage outside of development scenarios +# is not advised and could lead to unpredictable results. + +import threading +import traceback + +from cereal import messaging +from openpilot.common.gps import get_gps_location_service +from openpilot.common.params import Params +from openpilot.common.realtime import config_realtime_process +from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_controller.common import Policy +from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_controller.speed_limit_resolver import SpeedLimitResolver +from openpilot.sunnypilot.mapd.live_map_data import get_debug + + +def excepthook(args): + get_debug(f'MapD: Threading exception:\n{args}') + traceback.print_exception(args.exc_type, args.exc_value, args.exc_traceback) + + +def live_map_data_sp_thread(): + config_realtime_process([0, 1, 2, 3], 5) + + params = Params() + gps_location_service = get_gps_location_service(params) + + while True: + live_map_data_sp_thread_debug(gps_location_service) + + +def live_map_data_sp_thread_debug(gps_location_service): + _sub_master = messaging.SubMaster(['carState', 'livePose', 'liveMapDataSP', 'longitudinalPlanSP', gps_location_service]) + _sub_master.update() + + v_ego = _sub_master['carState'].vEgo + long_spl = _sub_master['longitudinalPlanSP'].speedLimit + _policy = Policy.car_state_priority + _resolver = SpeedLimitResolver(_policy) + _speed_limit, _distance, _source = _resolver.resolve(v_ego, long_spl, _sub_master) + print(_speed_limit, _distance, _source, " <-> ", long_spl) + + +def main(): + threading.excepthook = excepthook + live_map_data_sp_thread() + + +if __name__ == "__main__": + main() diff --git a/sunnypilot/mapd/live_map_data/osm_map_data.py b/sunnypilot/mapd/live_map_data/osm_map_data.py new file mode 100644 index 0000000000..5f74a5d011 --- /dev/null +++ b/sunnypilot/mapd/live_map_data/osm_map_data.py @@ -0,0 +1,51 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import json +import platform + +from openpilot.common.params import Params +from openpilot.sunnypilot.mapd.live_map_data.base_map_data import BaseMapData +from openpilot.sunnypilot.navd.helpers import Coordinate + + +class OsmMapData(BaseMapData): + def __init__(self): + super().__init__() + self.params = Params() + self.mem_params = Params("/dev/shm/params") if platform.system() != "Darwin" else self.params + + def update_location(self) -> None: + if self.last_position is None or self.last_altitude is None: + return + + params = { + "latitude": self.last_position.latitude, + "longitude": self.last_position.longitude, + "altitude": self.last_altitude, + } + + self.mem_params.put("LastGPSPosition", json.dumps(params)) + + def get_current_speed_limit(self) -> float: + return float(self.mem_params.get("MapSpeedLimit", encoding='utf8') or 0.0) + + def get_current_road_name(self) -> str: + return self.mem_params.get("RoadName", encoding='utf8') or "" + + def get_next_speed_limit_and_distance(self) -> tuple[float, float]: + next_speed_limit_section_str = self.mem_params.get("NextMapSpeedLimit", encoding='utf8') + next_speed_limit_section = json.loads(next_speed_limit_section_str) if next_speed_limit_section_str else {} + next_speed_limit = next_speed_limit_section.get('speedlimit', 0.0) + next_speed_limit_latitude = next_speed_limit_section.get('latitude') + next_speed_limit_longitude = next_speed_limit_section.get('longitude') + next_speed_limit_distance = 0.0 + + if next_speed_limit_latitude and next_speed_limit_longitude: + next_speed_limit_coordinates = Coordinate(next_speed_limit_latitude, next_speed_limit_longitude) + next_speed_limit_distance = (self.last_position or Coordinate(0, 0)).distance_to(next_speed_limit_coordinates) + + return next_speed_limit, next_speed_limit_distance diff --git a/sunnypilot/mapd/live_map_data/standalone.py b/sunnypilot/mapd/live_map_data/standalone.py new file mode 100644 index 0000000000..f73ecc7724 --- /dev/null +++ b/sunnypilot/mapd/live_map_data/standalone.py @@ -0,0 +1,42 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +# DISCLAIMER: This code is intended principally for development and debugging purposes. +# Although it provides a standalone entry point to the program, users should refer +# to the actual implementations for consumption. Usage outside of development scenarios +# is not advised and could lead to unpredictable results. + +import threading +import traceback + +from openpilot.common.realtime import Ratekeeper, config_realtime_process +from openpilot.sunnypilot.mapd.live_map_data import get_debug +from openpilot.sunnypilot.mapd.live_map_data.osm_map_data import OsmMapData + + +def excepthook(args): + get_debug(f'MapD: Threading exception:\n{args}') + traceback.print_exception(args.exc_type, args.exc_value, args.exc_traceback) + + +def live_map_data_sp_thread(): + config_realtime_process([0, 1, 2, 3], 5) + + live_map_sp = OsmMapData() + rk = Ratekeeper(1, print_delay_threshold=None) + + while True: + live_map_sp.tick() + rk.keep_time() + + +def main(): + threading.excepthook = excepthook + live_map_data_sp_thread() + + +if __name__ == "__main__": + main() diff --git a/sunnypilot/mapd/mapd_installer.py b/sunnypilot/mapd/mapd_installer.py new file mode 100755 index 0000000000..ef7a3389e3 --- /dev/null +++ b/sunnypilot/mapd/mapd_installer.py @@ -0,0 +1,152 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +#!/usr/bin/env python3 +import logging +import os +import stat +import time +import traceback +import requests +from pathlib import Path +from urllib.request import urlopen + +from cereal import messaging +from openpilot.common.params import Params +from openpilot.sunnypilot.mapd.mapd_manager import MAPD_PATH, MAPD_BIN_DIR +from openpilot.system.hardware.hw import Paths +from openpilot.system.ui.spinner import Spinner +from openpilot.system.version import is_prebuilt +import openpilot.system.sentry as sentry + +VERSION = 'v1.9.0' +URL = f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{VERSION}/mapd" + + +class MapdInstallManager: + def __init__(self, spinner_ref: Spinner): + self._spinner = spinner_ref + + def download(self) -> None: + self.ensure_directories_exist() + self._download_file() + self.update_installed_version(VERSION) + + def check_and_download(self) -> None: + if self.download_needed(): + self.download() + + @staticmethod + def download_needed() -> bool: + return not os.path.exists(MAPD_PATH) or MapdInstallManager.get_installed_version() != VERSION + + @staticmethod + def ensure_directories_exist() -> None: + if not os.path.exists(Paths.mapd_root()): + os.makedirs(Paths.mapd_root()) + if not os.path.exists(MAPD_BIN_DIR): + os.makedirs(MAPD_BIN_DIR) + + @staticmethod + def _safe_write_and_set_executable(file_path: Path, content: bytes) -> None: + with open(file_path, 'wb') as output: + output.write(content) + output.flush() + os.fsync(output.fileno()) + current_permissions = stat.S_IMODE(os.lstat(file_path).st_mode) + os.chmod(file_path, current_permissions | stat.S_IEXEC) + + def _download_file(self, num_retries=5) -> None: + temp_file = Path(MAPD_PATH + ".tmp") + download_timeout = 60 + for cnt in range(num_retries): + try: + response = requests.get(URL, stream=True, timeout=download_timeout) + response.raise_for_status() + self._safe_write_and_set_executable(temp_file, response.content) + # No exceptions encountered. Safe to replace original file. + temp_file.replace(MAPD_PATH) + return + except requests.exceptions.ReadTimeout: + self._spinner.update(f"ReadTimeout caught. Timeout is [{download_timeout}]. Retrying download... [{cnt}]") + time.sleep(0.5) + except requests.exceptions.RequestException as e: + self._spinner.update(f"RequestException caught: {e}. Retrying download... [{cnt}]") + time.sleep(0.5) + + # Delete temp file if the process was not successful. + if temp_file.exists(): + temp_file.unlink() + logging.error("Failed to download file after all retries") + + @staticmethod + def update_installed_version(version: str) -> None: + Params().put("MapdVersion", version) + + @staticmethod + def get_installed_version() -> str: + return Params().get("MapdVersion", encoding="utf-8") or "" + + def wait_for_internet_connection(self, return_on_failure: bool = False) -> bool: + max_retries = 10 + for retries in range(max_retries + 1): + self._spinner.update(f"Waiting for internet connection... [{retries}/{max_retries}]") + time.sleep(2) + try: + _ = urlopen('https://sentry.io', timeout=10) + return True + except Exception as e: + print(f'Wait for internet failed: {e}') + if return_on_failure and retries == max_retries: + return False + + return False + + def non_prebuilt_install(self) -> None: + sm = messaging.SubMaster(['deviceState']) + metered = sm['deviceState'].networkMetered + + if metered: + self._spinner.update("Can't proceed with mapd install since network is metered!") + time.sleep(5) + return + + try: + self.ensure_directories_exist() + if not self.download_needed(): + self._spinner.update("Mapd is good!") + time.sleep(0.1) + return + + if self.wait_for_internet_connection(return_on_failure=True): + self._spinner.update(f"Downloading pfeiferj's mapd [{install_manager.get_installed_version()}] => [{VERSION}].") + time.sleep(0.1) + self.check_and_download() + self._spinner.close() + + except Exception: + for i in range(6): + self._spinner.update("Failed to download OSM maps won't work until properly downloaded!" + + "Try again manually rebooting. " + + f"Boot will continue in {5 - i}s...") + time.sleep(1) + + sentry.init(sentry.SentryProject.SELFDRIVE) + traceback.print_exc() + sentry.capture_exception() + + +if __name__ == "__main__": + spinner = Spinner() + install_manager = MapdInstallManager(spinner) + install_manager.ensure_directories_exist() + if is_prebuilt(): + debug_msg = f"[DEBUG] This is prebuilt, no mapd install required. VERSION: [{VERSION}], Param [{install_manager.get_installed_version()}]" + spinner.update(debug_msg) + install_manager.update_installed_version(VERSION) + else: + spinner.update(f"Checking if mapd is installed and valid. Prebuilt [{is_prebuilt()}]") + install_manager.non_prebuilt_install() diff --git a/sunnypilot/mapd/mapd_manager.py b/sunnypilot/mapd/mapd_manager.py new file mode 100644 index 0000000000..e5f43f5d4b --- /dev/null +++ b/sunnypilot/mapd/mapd_manager.py @@ -0,0 +1,146 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import json +import time +import platform +import os +import glob +import shutil + +from openpilot.common.basedir import BASEDIR +from openpilot.common.params import Params +from openpilot.common.realtime import Ratekeeper, config_realtime_process +from openpilot.common.swaglog import cloudlog +from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert +from openpilot.sunnypilot.mapd.live_map_data.osm_map_data import OsmMapData +from openpilot.system.hardware.hw import Paths + +# PFEIFER - MAPD {{ +params = Params() +mem_params = Params("/dev/shm/params") if platform.system() != "Darwin" else params +# }} PFEIFER - MAPD + +MAPD_BIN_DIR = os.path.join(BASEDIR, 'third_party/mapd_pfeiferj') +MAPD_PATH = os.path.join(MAPD_BIN_DIR, 'mapd') + + +def get_files_for_cleanup() -> list[str]: + paths = [ + f"{Paths.mapd_root()}/db", + f"{Paths.mapd_root()}/v*" + ] + files_to_remove = [] + for path in paths: + if os.path.exists(path): + files = glob.glob(path + '/**', recursive=True) + files_to_remove.extend(files) + # check for version and mapd files + if not os.path.isfile(MAPD_PATH): + files_to_remove.append(MAPD_PATH) + return files_to_remove + + +def cleanup_old_osm_data(files_to_remove: list[str]) -> None: + for file in files_to_remove: + # Remove trailing slash if path is file + if file.endswith('/') and os.path.isfile(file[:-1]): + file = file[:-1] + # Try to remove as file or symbolic link first + if os.path.islink(file) or os.path.isfile(file): + os.remove(file) + elif os.path.isdir(file): # If it's a directory + shutil.rmtree(file, ignore_errors=False) + + +def request_refresh_osm_location_data(nations: list[str], states: list[str] = None) -> None: + params.put("OsmDownloadedDate", str(time.time())) + params.put_bool("OsmDbUpdatesCheck", False) + + osm_download_locations = json.dumps({ + "nations": nations, + "states": states or [] + }) + + print(f"Downloading maps for {osm_download_locations}") + mem_params.put("OSMDownloadLocations", osm_download_locations) + + +def filter_nations_and_states(nations: list[str], states: list[str] = None) -> tuple[list[str], list[str]]: + """Filters and prepares nation and state data for OSM map download. + + If the nation is 'US' and a specific state is provided, the nation 'US' is removed from the list. + If the nation is 'US' and the state is 'All', the 'All' is removed from the list. + The idea behind these filters is that if a specific state in the US is provided, + there's no need to download map data for the entire US. Conversely, + if the state is unspecified (i.e., 'All'), we intend to download map data for the whole US, + and 'All' isn't a valid state name, so it's removed. + + Parameters: + nations (list): A list of nations for which the map data is to be downloaded. + states (list, optional): A list of states for which the map data is to be downloaded. Defaults to None. + + Returns: + tuple: Two lists. The first list is filtered nations and the second list is filtered states. + """ + + if "US" in nations and states and not any(x.lower() == "all" for x in states): + # If a specific state in the US is provided, remove 'US' from nations + nations.remove("US") + elif "US" in nations and states and any(x.lower() == "all" for x in states): + # If 'All' is provided as a state (case invariant), remove those instances from states + states = [x for x in states if x.lower() != "all"] + elif "US" not in nations and states and any(x.lower() == "all" for x in states): + states.remove("All") + return nations, states or [] + + +def update_osm_db() -> None: + # last_downloaded_date = float(params.get('OsmDownloadedDate', encoding='utf-8') or 0.0) + # if params.get_bool("OsmDbUpdatesCheck") or time.time() - last_downloaded_date >= 604800: # 7 days * 24 hours/day * 60 + if params.get_bool("OsmDbUpdatesCheck"): + cleanup_old_osm_data(get_files_for_cleanup()) + country = params.get('OsmLocationName', encoding='utf-8') + state = params.get('OsmStateName', encoding='utf-8') or "All" + filtered_nations, filtered_states = filter_nations_and_states([country], [state]) + request_refresh_osm_location_data(filtered_nations, filtered_states) + + if not mem_params.get("OSMDownloadBounds"): + mem_params.put("OSMDownloadBounds", "") + + if not mem_params.get("LastGPSPosition"): + mem_params.put("LastGPSPosition", "{}") + + +def main_thread(): + config_realtime_process([0, 1, 2, 3], 5) + + rk = Ratekeeper(1, print_delay_threshold=None) + live_map_sp = OsmMapData() + + # Create folder needed for OSM + try: + os.mkdir(Paths.mapd_root()) + except FileExistsError: + pass + except PermissionError: + cloudlog.exception(f"mapd: failed to make {Paths.mapd_root()}") + + while True: + show_alert = get_files_for_cleanup() and params.get_bool("OsmLocal") + set_offroad_alert("Offroad_OSMUpdateRequired", show_alert, "This alert will be cleared when new maps are downloaded.") + + update_osm_db() + live_map_sp.tick() + rk.keep_time() + + +def main(): + main_thread() + + +if __name__ == "__main__": + main() diff --git a/sunnypilot/navd/helpers.py b/sunnypilot/navd/helpers.py new file mode 100644 index 0000000000..85894fb2ab --- /dev/null +++ b/sunnypilot/navd/helpers.py @@ -0,0 +1,189 @@ +from __future__ import annotations + +import json +import math +import numpy as np +from typing import Any, cast + +from openpilot.common.conversions import Conversions +from openpilot.common.params import Params + +DIRECTIONS = ('left', 'right', 'straight') +MODIFIABLE_DIRECTIONS = ('left', 'right') + +EARTH_MEAN_RADIUS = 6371007.2 +SPEED_CONVERSIONS = { + 'km/h': Conversions.KPH_TO_MS, + 'mph': Conversions.MPH_TO_MS, +} + + +class Coordinate: + def __init__(self, latitude: float, longitude: float) -> None: + self.latitude = latitude + self.longitude = longitude + self.annotations: dict[str, float] = {} + + @classmethod + def from_mapbox_tuple(cls, t: tuple[float, float]) -> Coordinate: + return cls(t[1], t[0]) + + def as_dict(self) -> dict[str, float]: + return {'latitude': self.latitude, 'longitude': self.longitude} + + def __str__(self) -> str: + return f'Coordinate({self.latitude}, {self.longitude})' + + def __repr__(self) -> str: + return self.__str__() + + def __eq__(self, other) -> bool: + if not isinstance(other, Coordinate): + return False + return (self.latitude == other.latitude) and (self.longitude == other.longitude) + + def __sub__(self, other: Coordinate) -> Coordinate: + return Coordinate(self.latitude - other.latitude, self.longitude - other.longitude) + + def __add__(self, other: Coordinate) -> Coordinate: + return Coordinate(self.latitude + other.latitude, self.longitude + other.longitude) + + def __mul__(self, c: float) -> Coordinate: + return Coordinate(self.latitude * c, self.longitude * c) + + def dot(self, other: Coordinate) -> float: + return self.latitude * other.latitude + self.longitude * other.longitude + + def distance_to(self, other: Coordinate) -> float: + # Haversine formula + dlat = math.radians(other.latitude - self.latitude) + dlon = math.radians(other.longitude - self.longitude) + + haversine_dlat = math.sin(dlat / 2.0) + haversine_dlat *= haversine_dlat + haversine_dlon = math.sin(dlon / 2.0) + haversine_dlon *= haversine_dlon + + y = haversine_dlat \ + + math.cos(math.radians(self.latitude)) \ + * math.cos(math.radians(other.latitude)) \ + * haversine_dlon + x = 2 * math.asin(math.sqrt(y)) + return x * EARTH_MEAN_RADIUS + + +def minimum_distance(a: Coordinate, b: Coordinate, p: Coordinate): + if a.distance_to(b) < 0.01: + return a.distance_to(p) + + ap = p - a + ab = b - a + t = np.clip(ap.dot(ab) / ab.dot(ab), 0.0, 1.0) + projection = a + ab * t + return projection.distance_to(p) + + +def distance_along_geometry(geometry: list[Coordinate], pos: Coordinate) -> float: + if len(geometry) <= 2: + return geometry[0].distance_to(pos) + + # 1. Find segment that is closest to current position + # 2. Total distance is sum of distance to start of closest segment + # + all previous segments + total_distance = 0.0 + total_distance_closest = 0.0 + closest_distance = 1e9 + + for i in range(len(geometry) - 1): + d = minimum_distance(geometry[i], geometry[i + 1], pos) + + if d < closest_distance: + closest_distance = d + total_distance_closest = total_distance + geometry[i].distance_to(pos) + + total_distance += geometry[i].distance_to(geometry[i + 1]) + + return total_distance_closest + + +def coordinate_from_param(param: str, params: Params = None) -> Coordinate | None: + if params is None: + params = Params() + + json_str = params.get(param) + if json_str is None: + return None + + pos = json.loads(json_str) + if 'latitude' not in pos or 'longitude' not in pos: + return None + + return Coordinate(pos['latitude'], pos['longitude']) + + +def string_to_direction(direction: str) -> str: + for d in DIRECTIONS: + if d in direction: + if 'slight' in direction and d in MODIFIABLE_DIRECTIONS: + return 'slight' + d.capitalize() + return d + return 'none' + + +def maxspeed_to_ms(maxspeed: dict[str, str | float]) -> float: + unit = cast(str, maxspeed['unit']) + speed = cast(float, maxspeed['speed']) + return SPEED_CONVERSIONS[unit] * speed + + +def field_valid(dat: dict, field: str) -> bool: + return field in dat and dat[field] is not None + + +def parse_banner_instructions(banners: Any, distance_to_maneuver: float = 0.0) -> dict[str, Any] | None: + if not len(banners): + return None + + instruction = {} + + # A segment can contain multiple banners, find one that we need to show now + current_banner = banners[0] + for banner in banners: + if distance_to_maneuver < banner['distanceAlongGeometry']: + current_banner = banner + + # Only show banner when close enough to maneuver + instruction['showFull'] = distance_to_maneuver < current_banner['distanceAlongGeometry'] + + # Primary + p = current_banner['primary'] + if field_valid(p, 'text'): + instruction['maneuverPrimaryText'] = p['text'] + if field_valid(p, 'type'): + instruction['maneuverType'] = p['type'] + if field_valid(p, 'modifier'): + instruction['maneuverModifier'] = p['modifier'] + + # Secondary + if field_valid(current_banner, 'secondary'): + instruction['maneuverSecondaryText'] = current_banner['secondary']['text'] + + # Lane lines + if field_valid(current_banner, 'sub'): + lanes = [] + for component in current_banner['sub']['components']: + if component['type'] != 'lane': + continue + + lane = { + 'active': component['active'], + 'directions': [string_to_direction(d) for d in component['directions']], + } + + if field_valid(component, 'active_direction'): + lane['activeDirection'] = string_to_direction(component['active_direction']) + + lanes.append(lane) + instruction['lanes'] = lanes + + return instruction diff --git a/sunnypilot/selfdrive/assets/offroad/icon_map.png b/sunnypilot/selfdrive/assets/offroad/icon_map.png new file mode 100644 index 0000000000..82c0236a48 --- /dev/null +++ b/sunnypilot/selfdrive/assets/offroad/icon_map.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57a92adcf88c7223b07697f8c2b315f4f4a34b32a866284610d5250144863c6f +size 28235 diff --git a/system/hardware/hw.py b/system/hardware/hw.py index 9722e35e30..f582def482 100644 --- a/system/hardware/hw.py +++ b/system/hardware/hw.py @@ -77,3 +77,11 @@ class Paths: return str(Path(Paths.comma_home()) / "community" / "crashes") else: return "/data/community/crashes" + + + @staticmethod + def mapd_root() -> str: + if PC: + return str(Path(Paths.comma_home()) / "media" / "0" / "osm") + else: + return "/data/media/0/osm" diff --git a/system/manager/manager.py b/system/manager/manager.py index 9afe1a4f26..cdad098d2d 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -18,6 +18,8 @@ from openpilot.common.swaglog import cloudlog, add_file_handler from openpilot.system.version import get_build_metadata, terms_version, training_version from openpilot.system.hardware.hw import Paths +from openpilot.sunnypilot.mapd.mapd_installer import VERSION + def manager_init() -> None: save_bootlog() @@ -54,6 +56,7 @@ def manager_init() -> None: ("MadsMainCruiseAllowed", "1"), ("MadsSteeringMode", "0"), ("MadsUnifiedEngagementMode", "1"), + ("MapdVersion", f"{VERSION}"), ("MaxTimeOffroad", "1800"), ("Brightness", "0"), ("ModelManager_LastSyncTime", "0"), diff --git a/system/manager/process_config.py b/system/manager/process_config.py index af150e47df..2e3f754a76 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -6,6 +6,9 @@ from cereal import car, custom from openpilot.common.params import Params from openpilot.system.hardware import PC, TICI from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess +from openpilot.system.hardware.hw import Paths + +from openpilot.sunnypilot.mapd.mapd_manager import MAPD_PATH from sunnypilot.models.helpers import get_active_model_runner from sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, use_sunnylink_uploader @@ -157,6 +160,10 @@ procs += [ # Backup PythonProcess("backup_manager", "sunnypilot.sunnylink.backups.manager", and_(only_offroad, sunnylink_ready_shim)), + + # mapd + NativeProcess("mapd", Paths.mapd_root(), [MAPD_PATH], always_run), + PythonProcess("mapd_manager", "sunnypilot.mapd.mapd_manager", always_run), ] if os.path.exists("./github_runner.sh"): diff --git a/third_party/mapd_pfeiferj/README.md b/third_party/mapd_pfeiferj/README.md new file mode 100644 index 0000000000..a814f11260 --- /dev/null +++ b/third_party/mapd_pfeiferj/README.md @@ -0,0 +1,2 @@ +# MAPD implementation by pfeiferj +https://github.com/pfeiferj/openpilot-mapd/releases/ diff --git a/third_party/mapd_pfeiferj/mapd b/third_party/mapd_pfeiferj/mapd new file mode 100755 index 0000000000000000000000000000000000000000..d24d9b757fb67bc6f297eadddb8844452f844647 GIT binary patch literal 8192000 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 z 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 z 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

    BshK?*gwf067bL-;9d`ZDF-Kk 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

  1. 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

    oQe+YUE!E3|^a)z*%QgyC^ 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∈7CYwxZ9-QfCZfm$;ooQfM4J@u*Q+uwT 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

    Yq=Z`Ni{HJgI#nZF6yDZ4u%bXy- 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≫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 z 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

    K)-5r7(XVb65X1>}|dItE9f$ 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⪼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�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�RTL+N0-dllET(@0N7nWo_2NI#KnS9O#)EKSepf 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

    dJ$D=&;Kn*(0>RqXG2&O;Zk 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�K28H8vLc8;AUjNB$-vf0L2F zGUP9c{8b`<(~!UE$lna)Zx-^m^E0I#HT~?v(SaHAccn|~_-y2F8}hdS`Fq63-)1j1 zPzt|?sVQ(Qm~{v7zBS0zdgKcFE8j%67V;}0*hX#ZskLKP@@%R_%Q#!g9-Vl#phtF^wxI4mg zr|xK_*8Xs zojqHfnd`N2E#EEXL)@3#nc#u*?9Q1X=23^8!@0qk#8y+piMQr|@q4>l?|a4X~SAxC1c+#@ZRXn8#`joI2!*5kV>fH>1 z&O_0Ap09TewkW*N^?bOa3zdVUF=DjWp6p|XI-ULF25h?!xgKu6PR#5W&%i;~-sm9d zgtIq(C&#>>2S=InP1k$sWR7uowX>5mCB2EKZa?2n@GJq(v!_z3ItQYkhlh(f6DfK4 z$v4ycwoOKU3+5~!viQ!Z2#b<2CcWlFlOyFC^@v&O)t&;dy zt@u}?p__?0P*<^APVaG}Eq%wX!HXr}`;&`E9Ff z(XXCu6+8)>k+xxM#op)x`6TODpZ3m%x3Mo=JCU&`+E0FtHEK3%6#Kj41#gY|CAkse zg$?kSu@m7H1BdKH!8;K>+#4H>b*sk=!cOG5e=eilx$I-kVdi1h@QbXOqnS&u;k{M|_}e>xo|H5x;{;56`1X7xSLFkW|+^?3;!@pbInm%jGbd+r$x z%zLLHSIqNS*`4sd)`SLMJ)l-_Cc9HLp`X4Sn^W|dhHd5Hfwe-oQ!eH@*5ZZWZZxrpVe~zJyJWl?+eZ7$^7*IIr#=gYV#be> zD_YVQIG}TcTu~1eZ?2*1sWrq|?@&j5Y0a6DH|jW71AomlIYaRBsrNYg=eEW8OYHY} zd;G<2O_z<_Wf8;H^Vy%!~i+`xaXbyF2!(oTN-`n3Mx_g;!?W~n(ZA6J!1@mNhX^jc*D+nS zfivMB%X%dEHCO43IoR@}IS)3B@srSU9x@y3mA860GJ75WHlPoN3!mUE4$izBr#&g^ z#?ijsrH2>aU+U7M-lfM<=wbY}toL_*l>7aga=h=v&-)mA0UP)A4d|s`FPJ~6$fuVI z(Mzhqo`_nhk?5zr&|@1lM5UpJemBLO6M$A_BtPR>F8hQMTZhsM|pm3DeYv}O>yly;%=#(=qTe8yX%D~ zx97XKhz@Y&b^tsF&B_P!7v3tjLZ#*CbFF6{Uo@ZxOQ73OcN}BqG@Wk`lOH19zdX(R zlD}k4d8%bTF_&&Xvol4>OvLhqS%8TxBoHt#Luy+u90_om)M--nSw$!)pICtgl# zjK1X$b|^oF&&EcAXN-*m&omfYyBGR_d`sq{T+|2ntGLW&{x^`y$#)@E=A}E5dh|5_U)WzKpE>*z!VT zWUSezS~=IrXJ1)GK6^z!N7n?;1^3`wdM5@Pao|XdMSnh5UYcaB@#{MO8GY}0Sh4h8 z=i}oA@jt%3rAo8_#walh_QqD$atCoMe9+(|k;ZHOE!Eo^@&iI9O&iKGz!MUM3u-^p3b-)mUuHI*ZL*P@bCc`)QqJqKj7vrlo zy9Z0KIE^32A8>4A*}w~-)jIiGKKx@3WaRUAc7~c|)BX%TsK$5o*FTQnk6ZV;HB9hn zq?=U}=@)l=*!V}<3m%iB7@OeaGiKT02CDRC;ijs z`D5s4<>@4_+mhIdI!n_U3YEsu&50TOLuWbb-!S{LV>yokuQ+*Z?m1ZLw2XD~4Es^q zHD%TC(%F0B=uakT-@+P}n1c_D4Z6z;aQ|JQa~2++(>b>`cvNSVwa(PKXUTPkO2H)J z3D8rvvv|koeE38%)5?1Z_?0{PmYcl{aOc=>x|6G5<51Q`Q+FNxlz^`C74^QOyPZ7Q z&d$PS)gMy*q*iooYm~SfJZ=5jUjxIT%jEIYz!03w2YZLL{1p3La(8rQKOcK)sQ>I1 zedfV)eGpk;Y9m6t>9gB0;dP9Zx-3{LHM;bs-fZFwC%ItR6Ia`9!`HO zoIa*4?P-7L z{JtG~H4e^W;8nV`0z16|+rq>U;Kg5z#wG{9S_9DWMy9;;eU46aYRkl+kzZCkeu&fO zxgH$!*By>CZ0S3zko;MAMSC1w@4{badGF?&!`b$a`MpVZB(OFrk67cGwVttxnRA47 zN$W&ddI{Oqvl8-iRBJ*tx}CMiwd0ZLdVX&L2lDqa=94|Y6aGD`zbw8Hp9FL8$?dLw zRzA*XlP{l+2gNjB(QXibhv27OCAvB5vtlA^@$Zo#2OmS6xxe!r?URR^6-)ARj1qjV z*EwIE71(LnKO5z&C+t^dvptta#sr>`~i)6@GuI@bH{yfxn!ew5Zvx?{p9BcDEc z5%@pp-~D_*z2?B2l>_I^o%Jm}2V!$K=Yk>R$hjx8g5e<_3|@Uq@AvH?=RBjQAMa)l z+kZZ#E58n2&_4UG4!f~IaHw~^el*T<-#Bmk=Bhb+{WqKXuBWGKaZ0X{Z_!vZ61}Ah zf3_$c&qIeow=?WFN8=yji%V9qx4&Pc*t24q)D4?!U2g6v)Smhs)+F;;Hn!{>*~VH& z*R$stga3cN5W1pA;>e;CSH;KY{7TZqKF+gSXrowx+Ui}!u}3YHf9&kTR1X&q=9yfe zQ=yah+^KcRJIJEWlvI*m;Mb#O&2V|w|J=i&cc!G&hsQ*J;PuOg;g{U;;d+n1*IV|% zkvaGqp7GPn8}F>F@f5@M=ykmrFMa=?#}Chd3 z<^Rrt?TDjqwl18Y2tO-_FAGMi{YQUFexH!0ExN)z`_%)#S7(hWe)sgCXsiFUHu|4= z@a*eBUr*@dJc8>RRk6MfqfVZu7t|MfjR(UsKWkS+4>vZuuvTH$E2nzI8$J8P(yJF` z!JA}#k`LVT{S@8TPvdh)*Gk92ms(5a5&M)LYXV;Rg4Nvps(clTH6V^{q5D}BgDd8D z$4+qh1Rj*{kbnn2{3LVU3HDaA=KWgu$OJo&c`wYGcl+*R&-zv7Q8<=2E1nd?*YbK{L>SE-v$I2mUtryzw~j z-Trrgk9rN6^2fOnKYTj`-|q(6bAj*0Ech-v4t)RgDd3Zgd%mpnhW83-2q|R$)!LIE>xG-p?WJ;qiHQG5^$`Unjj)LOq5-Q@ZLfc>KlwQ%?I$ zHr0$9J$od#9WXR&p4s=e$Ug7UKE#$vverDl@fc^1v1i4Pfvj<)&-V7PzK%BcFQn}c z=`Y1NG*%0?VgtV2^Q`9&aTl@b@Wt8N5RMx@-t+4Q{H;1-=Sj{)3;?$;(C!89Bd#N- zp&UFd!w0+*To)00Q5)HOUSD2YQ#%FPgowSBGgg^zj3{I5#HRGdP=4o5#fiUC_WiGO-p`dc z@7W5T2^Svk8M_M_v|yWv4r_jpTfZu2Li}`y8VhB?0S88(r~T{R8T;4q8t5>GwPakt z+^I^PB3om5cS8>D&78)uCtSliF(;C$9?jkuHcdY9ywKWI^}wePsh zIVkqj=FY)#=ioitMIYMl-cEgH)u3#T5~K5tJA!c)6I{oh$is}Cp4@GEVH&w860VzdM5Xx7FTeuQ9tgHJgGUs9fk?t9fpZjr*Vhj>D*y> zMssWzHW4}~{u1+;Y^B~E#r>${vpxGU`Ky({>+;D`c%qBWW%XP8aaJe99jy7>!CJ^z z$yGz?^Gy0Yi_f$9d@Y~9%I8z~d@7&Q_gVIIpJKAnoOwOagq~6DaOHdHK4G2RNUR3e z{fIdQt&7_}px!)tX6C+Q;V)TWKTXU_&vy|Y`Zar3FCb&`o!i@?(|gFs0VlTT?{_x! zMXtw2)Lqe4jJdBel9JBT-!bKp(lI(yTH@M`lO4NJzo}M_a)I0NTlO!ZEqe6IzwY_& zmE66oyBhXRhi|9B6YPV2UvS}fw@(9x>Es&lzTTO3QY3ZNK<-mH#eqw8NcP|C)FS!Z zS?GiibTP88yffAP*Zs!*$eHY8_EOr`T-CgH!xx(OZ~7uM?$74|d>+`WT-5dkVB7-? zNqF)-;An@w2Z+6VU=fGs_Zn!u;JOTYE`Xjx;Un>-_K6GF>(zca{@ca`_^mg4zL(hF zs3c}FgZG!uhPKy2+pjhUdRfh(0Rea+*nADT$gih9;k;y%skMEw9oKnG=;^FMp^b}g z`>^qW;hDD8sjubG@#ywZ_h;(%d$a0UB?md{fLGr{_wf2{7SEn=^3l@qnj~~mjNJd; z(#R)`Yc1k3z#1a|xW<}jD|f;8za!cI<{9Z4)N;^V%zlQ^3vXP=J}uv6r)e)tzHJiO zl5gVeUpV)3YX5@$UvyRy{?ghn-$Q%D(jnr@s+Y*Og;qVrRowZ?e%IO;Ta}3)&!>{z zzN2gXbM}k&nV4kv{i3eDBH(F>(l7c^wu$tD@|B(eFGg1YqvFo;rQ%WI&3ZnBcEBG( zcPIv6)+=C>4LQigU1A7vF62^iDE+Rv*CMYbZ)&{cN-^DhpHqsC+4=xK1|Shmjp9ecs4KkVFJHYgqATg1E;gP$ef zsgBR+{+1cw2tE=&DW5}oB8!9-Ai?nVf|$ct!Xc3ckb(N%U_n zzWJ*7hIz}bNJ5_$bV|!?{(@KS^|jP6KJ?LDBI2cLcquWP@u5```)s>x32?8tp78kv zVl;935f2*rfLGCHTUmEG&^-c14&aTRTE}oMbWz{eZ2D^??|T`-2@$^PwK9=4(-Jb zqGsvqYd9Nw1$?Pz+j({=<7;oC8JoC{oZB#eWgBW9Yw+z`@a>b(Nato$H%Bs2w*a4? z+(JFCVLwC9Pv?1qHOYLwi+AM*=(*n8MnA3eldoKF<}i#o6f=i%7q?@98~nRxN5~08 zPZ?gI|6=l`HIDi+&*@M0_r(>|1cx6@y&`1O$O$;OfahKO1@Fb-!6zQhysNRwoQt~D zeusVUYWIvqx$&h%GXNFS6ioU%YI@Gl1cGT1bo(j z&k>vlY-PQYp7+PxcT=}Hh8~MoBAZ%+=%*H*9FD!c=O%FMem8a@`doZ98vdl_*5J3w z!8LLcM=ne|mj~};Jmr4WONW`yj(w2e{r!yh3h#B%0@^4?;c946Bwv3T-z%99^x3BI zXHX{wpH1gumNH+p5f6I)nq)vQDL&Mq^E+07ed9RROWreUDLgZRygbRLmk)Up`8D#n zzH`gGBRacTf6Is?)%a?h>64#kVX zr*|7$%(veHeRWn=wBCsg8X)&F?An^5x9rd)@+#k$^>?N0Q00Bf7M+5hEdTUE%@<_KnsN??1mi zqB{&f>F={gkT1l({1o=nN@J$O63isw_7X8 z>8FqNE}sP9g$j5nwXEeYetfor`l%)_w@Ch3AKP&zzG6&#G@Jc^i=z zSpLw#+uwMoigVJ$XWF6NE}pkTn_a$su47GYLr>^T;T`m+&%}+o?&sR%$z?7Z2-^K% zWhU)@Kznp*X~42dw}+fKx4xGX&-9*o@_K#N{w?iN)yl`g){#vgF?EHC>?(3x;zQsi zw_ojdDrZ*u#5-Rw!^MTx`eE=~vBz^+IS-yJ&Ch~6*M3KH4t)|tU*^LnlC6h{af$Er zuaI#C{~Y-0S*_jIV|+XycuB-ZAzEG}D@U zk@$qRT8oWN;hmmz%4Z~dC#X|KWZ^;nZJ78MwAz}cU;S3-H^F;$V)Q%5K1gncLqDhA zT=YwlXCPkwwDkLEMJC_nqTh@E3;MM}3+YWq$185^(eE7l(et1QbdVnGk$%1Mm)9Kr z_8X$%bLjd{OT)Fww>Uu>KADA!?aa02ZAJOx*JDJbbCrHl)CqU23{swwplZA_H zdKO!e|3vNnrzfBK-mAFy>)(YOBz(zhgQPXBlG z9cKUc*iRv^zV-AVI`z}a>nqA9I6--h-FIwx1@C`_aDVmD_piN*t33L4J$K3574Q2L z_Ac}-u^&_p5Ay4clZ(EM%#!4(&z(zidDi$n-8XW5j`8Ob zJIV#apc8~)WEKoL^J`6fI-Osuam!U3D(#=auV>pwlrsgt=EBch{D!^ck_bP-OD-Q>gN^4~^f`ET;wbk@-4zp);jZ9h83 z(LeIvbiUB@0sQ{ki&p+iSMqOvsLxBf5Bs5y@3|+APZzYM@nhxXxct5Ycr)TFJ;aS3&B9l%{W-;toc)M2y<^N>G4NV) z)fJEUO-(xQe!F|-J9QT4nDE*Cdnw*I8e!NxG6C}*w?PxUf+W8QBOezU+s#&6s?76VVk;$vu#SjgPLSL-5T`Z{;|^k?m` z?lzC6>t7G~=7)`+KNG!2{Kk{%U+&_rH@+W)FZ(|4)`JN-wfW+wP8 zI?DO2N_gk3CWz6qS?)(y~*!QOnT%wbDZN=8>b0$$6x7eB)J6i7C)5IN-lW&=Oq$*VOd+j-T zHQ8GqfBF3pH@u_TQ3ovIoiN{E{XXyf`*vTp>CyM+je7b0dHtN<%g?rE<`qXu*W4et zsh+j#UAJxPQpKR@b~_aC~~X&XP2wgV!!-gU$GcB}4tHQ!acmmG!7HLIqA zmypTBiWfywis8no1FYZXHcm|~2$2s;?pH^$4|3T*W4>$M`NqeRGqWPVzIxQeS`&5D z=z*RI#z@jvq8+>*2&Pm!TWx;GU;n)4o&U=Fmu-66%sb4!M1HTx*6Yr_clUMu9(#YA zRj_a=wnhF0kfCcKEUj_P2n2 zPcU`83;TTjKJ?rBcF%j|vG-fw`R)79zp~fBy@NYs@_>89WB2a9>%K?zY++c>fP2l$ zft#9$FW%+S$fNf|{a$_luE(8u@96Km|NM>L+dbl)$DH@|?6kIF?FkEQI1 zQ*Q=2cS>!=uJ-GX(SF0!)JwECJVzdHHr?|4bfdmi7tc+DZs?6Jx_toMJ_@FelCP#V zDgK&0WNU!^EPKkI|K$C~Vsf#&V0t7CliI(d_q;i5nwompgNZ!QY?$_tBPV?qmmG}r z!88Pz&|h6Jbplf;l={63)9?5jquzFWNJVNGYlm0Yap#zh#<`q@%dTskdgMcMrdzhg zR|A|YWWQDWi4_5zQTW!D=YL1-jKV1EwY9NI_qU*DuD;O8FO(glx+HnZFU0=PT9iO0 zV#+VHO6{*)iY~2*q&j$>J--dU`30z1Bm6v+3Hxi_T@?2NJGRF!)aEC&Y3b|0qkIx< zJoZhUy4j!MZgkmL8E|21WWzNgS=P)$vzXU3`o>16 z4$=qq+S~$chly6|+ViO$jXj~e`TlJe)ZX{7 zt+1myO1SGTtL|5siw`dGoS0m0^55Q3sI6BDq zMb>}=W6&YWYdFN*!_0jc^Nu&r&obuOKrR8c{L|>#%jT zX7kLOLu?%DHg#4L6OpUQtlMR*+fmkS>a4a;W8I$4x=o$c{j*rNXR~f6skK@{t>c=^ zTC4Ao>t^J44zl()mqQf-?mQteMvr1m~l zk=pl6MQZ=Hiqrw_0eE{?Me3cGDpK#hT9JB>+U~}V+&kHzDuZ|Nac~KQz)x9h?q6;FTD*aV+HE!{G9FXWCJ8)y-QzY%EjmlKi5D@Iz6A z_7UF0H^X0j?&HFXA7@^N_^o)ZWgjXN-=H^W(?Wk5|HdTsO{2)ie8wts#*$4Tzeqm2 zd{F7fQB@tv9og)!ZF&LiPGg3(P@+H`qR)9zIosH z-<>m!?ZW%>n78r@m1k>m8)pCuydHzsJ>Be|cd38g$cu8^m8+ zhy0O*)$yV4m_(j88|7K{2xQG_~l3o5Y;7 ze;xz>KkVbysPxVaF~0nUpSk0fDcV&*Jn3nLb+=aRBIKR#s=e8ck`JOvY z_W3Q*GGKBqojZZO`R+6Eru6vnLExM@YfTB_=M}NfUh(ms@6IJ>Aw-^(<`|$a#TCNj zM+rvZLuW3GP8sC-#y2?MrFX=aVQQtN*V9S%qtpWR@apKI;Kq{Edah+~jgM~9#UA{l zO|DrwAKt{t;Nx8Izi|}!k%42vKg5AQ1|H%=tyJ*p^gQqnCdQfHuqiwn>a=UZ_A$?D z)6ZfhybBH!w5<>x+_s&aoCT-$jQBM^Qf>GNx=Ri z_mF9g=j=TCF5uK(+Uevl37)V!u4t31|Eqlcd-xxBx`%&XKkmE?jf>N`z^_?3S9jL{ zgLuHJ&!U{p`~J~8bC{V^B{^tYmX^O2t}I9%wEk&5+8*lRK5xGqLmRK1-qn9y{E!tt z_2OwJ7Z4n_@UG4?B!{5)U7l^q#lOC}##mcTzB9b>tsyRsd~r0v?Zh@R=WBEh<9X-% zHu&bA=#Q>~PC7T9!Owp$_(eBw-S@%&@1BhP4e;N59QY4~LMGmE1-{YYP=02-WJ1|o@Zr0#pQ%9@B zJ~M1;nXU|TcWZ!{`YFYhiD{~ja`^(YnHTnH#M~zxAg7T!v%vNx>~qDrOnq6t`{%sI zgAe|=j~SNGj}M~f^PNm%Mh2&L9M04S`zTLIq$OG>AOn)B5=yr67LVwStWjp`1G4T zv+nMWZz2w^I#t>2e0nbtl75$;;N(?S$rr}&{1@(}%mv?j$dF_>0DN)av&>!4z3nd) zBLh!q-uMG0>}74LC|%pj9b0&CVHB-~4Pb6hfMa4_EzI8;llVP8qSlt5f}6xD?qF!7 zmgZ_|X|5?xwb5_;25KR5ufx7g<*EI|o(?=!o_hP4^3*%q%2V%#zQ`T~IsJLS#X9mH z_F#;8#~Js%Cn71kFR^&;Rf0HXLm#_l5Px^}vF8otZ>X<5za-MUkGA37R&&X~K=X*x zgUutuq2@R>LBnAy^|42@DQSGv7qD0IH_Y`QckNp~lCnJCV z`5hX>{6fsHkoomxe*Kx>Am%rO`3+@$Ma-|{81rMl;BD@cILco$zi2OKehs~x`R(l0 z^ZZJj`Q7WyPxc~wB{_>lu@|AiFs;SzGpm3z&wmG+9zLBp??I-L7I9(Zh4rgh>GnnJw1NMJ771*Bv_HDqv z6WDhF`%A$7YUbWokFf8|x9bIa-&xJcLDw|54*ha-d&yUF!~T{7`*2|Qf=D){JMf<)NmpT_t|uui=;VeI0&D_I3EBq+iec zvclmP@rAL`;frFe8Pa{MN3y5Ud8`8$q4T77LmMwd=eahE(LdGrV$wTb#lL-@oC1B8 z?c(J^%;Nde*ejmCGVSSK>sK4?WmlN-&UM$nb1iI(qK$d{uJtXBPhG6_Z4sYqXfp}^ zUFEJLT2oxRQthHAB=c$e`6b2)X%%P(--OG@z@^z&^VUxzWL*TS2K4X z-^tdFFSoF#oHOn^L*~UFJo`pAx7lxB9?_i!7yJ3n@Ed&N+7~Q$6S@0Bc8+6X=sd?I z)EhB4xSSXlYqHKN4`=PlXWnzz=X;hoQ7b&?#HZZ4_1Ld>a@TzT9%%)C>n?YA!#g`t z=<>!o`jOq)6hY^~hr*L!(Rd|Oh-q`aD@y)K0ets@HO~GHxV&RPaJlg9)j2;*PKIKb zQEZwL`nrr5rpY_#2mP6|bc%n@ac~^AtmRIh+~K16GTv(O{Stl?->g2qF8aL38^^-S zhTM30CY&2DJD7`fQU+c&91kz&=fun7Iq;HT?uxfoF;^qE(3AX+L+7zaYH%9mtQv6~ z-8rbUpn5*9*K-dv-J7?X`09bX@re}k57Ebz0oL+-Xfu^sEUH&j1}~^4VuX9Js(Bt= zP<^Bq&!e1!^3&_Pz@_`U8eDpP7kW)y5H@W*-BCxIk+f0lPr9R!HpXX)p6vK@@aH-3 z&oPqUnsw-p~3M7ANx2RPrDu& z%&0et-o}UCtUQC3iP8mm2P}9$IT@dov-!2`S?U?Gn9h?abAHpFrC@44`o4)(Nry`h z$VaK>9_1wcsqb3)UIQL8`ekpk$9~z{RKKfo^c%)U_3Ql|=;Xjv+?@t)rSAjSXTt4{ z-keK};OAfEey-y4)mG{n;KS!f-;cJ7$f06A+#KGXZ+w2yQ?#nq`paCjUY<<9`fU1! zUfI58AAT2a4+93{YcihKf1%fZB#^Uz)i(Nsd=sz#V&EPI{KNW+N9YH5&-KCk0l6^_ zyuEYwCwLRS{?tb{ynp8Rp5T4U>)$HO*}vf3?dv}myoR5>*f@OXk5BBmBQriR8+paw zH2-<=iQXo^Fr6=__-PY$-KP{%JM4b+^McwV^Fo|$ABs&m1lwX9ygr_BXlE%8kh6Ww z%mZ4QbFoWB%OR{4L!DYuKXvEtJ(E03zebQ%tzSCFs~Siq-Z_@MCdMOfkqU9XPknkZ zhI{G9Z(l$cN0!~V8#rcP`D`2ceJyN_vcLPQbj+h`zhaOhr?(0(%rD+ob_e4RilqL- zhZ`ZNi5%4^ZEC*es5N6 z2-&jicd2of3%&&|d?q#oeDPN!rHR?xJN{wklX2o4W-RsZ!UR2kVV>2dpVhnZE#Z0; zxHiAbZgJm!S!8Cr+IT z)nb4)x&h z&9jNQdw3QPYMIyuqU6sGT!=rd6XAF*zg2!7TagDc<{ z%@UP*2Ar%zj!#vzv5NPLibMakUbQaC|By|(lyy??{%}M`Bl&Cx!#tNB3B1+$r1E9r z;4n!&#@Y+0yMe4diY?-;iNJDEoZl2L_`}_9ynye+Xlra3JN;Vw3j{0s0X z{QlzqIeX(nuOSa7N?X?71n)g1_e zc`kddyc4}wgPqL2&#-=x=HY|Oo5QD7G$&T%Q}?i-Ilj8EIllZdYSc}oMqMSJd-1t9 zpZhc`f5Ys(Pjhe|XARQYkpPc(0H^lr*FnQrO~77v3VYtrD$ZJ?-=qB^sVM{Shw!Im z56T8lK*JW+AXDc}e8HNPKgHp*CTLrwJSg~GIq=&5kC&)zBsKp$Y9hI7hv8B5it4uR zJW@Cj+YdUQZi`Pw5B#9@ zc^4Wf=T&5L=;hR#X(m6^U!OvGVTn1!2L`ZjJIl(*Q_xPiK-q>fH;Miw;%CW->oUw~VTl9CXc^@5oV)I^oF8nrJxCj`X=$~(l zT%64B?w)*uTiXWy&G_BrFE1}>dtI8xWMePLvM)V*_{FUJAnDPB&Osl4 z#o_U6p1KpgXW;RCp)7cQk_AsJ2RvTAuimC!T=#m233$K6$fvW0XwUaq-GSwUP5CGu z|Ht_~8_qG^c;C@aZEFhQ_sih-sqlLxpM!i3@j0(~EqU&$%aXz8>!~3udh@&+pM#$y zyxxjl@bkIBPt@h-8NepmBcH;r|F>8c?!J(PJJne+`eLVBU(dui()Fp53$Z5_K|^## zyasyC#};7nTrs!|< z0CSA+TQTc(jCagkCbU0Ad`*oikFU@4-jV$F@Xn;HcT_LOj3c>o-vI_}ipUnlNB!fy zI5ty`nhMcXbAS=Ow+>sNd1#g`z;9a1-XwnjS>`O0)7IDmtlOUM?Ck8_T!bAG2RHI# z6myJ6`O6&R)A_48SMnEJ$?xx+!Dnz27C!>B=&HVbJcu3t8T-(Pu2}v`*#`mTTd>~A zR!bxUc2$AanoI1XjoES7g+7b>z+ECtx)43XB@p%ZU|zBoD*;<2w)s z3R@@H#-^TZr6RqYajJDLX?^;RutV2<*mxT41rPXnO@96Tzs&8=D<;u8)LGZoe>+p> zc=rR#ad7sHP?TPf|=_~ktY4=e5wycYbr{zHH}bIZ7M78}pR z>kQvOpJ8f)Y-;|djqam$+lcm}q2Xb#4SwPd)l1iN@u~Z~N&EUdGc@Iya4Wv(?Cit8 zzRlVX5}#?Gi}lse*W2gn51ljD+1}`sKIoLb=$9+_d?lZ!@j1Qz8eKCjTh|2a+Ck_V zo_EtVWsF^ku89gKPTs%PR7amU^*rN?fl=~!vxAQqdcfr1A@_n?u^-tkG3KJS3EJ)D zzJr+f8XlKl@9)p!31U6mg*?hWDHm^G=M-h}L-u`np1vP#?$Au@`#5qWU9Yt$X5@_T zz^wZM3&3>|GPw=gYy+{TcKm?$0c#%Uq-N4u;@o<^mhZ}M)tTGRfB1f53?DTC4YjT% z(6$u_>Rr%uazV@5sAmTF^nOur3BU>&x-O&{6`;A=%TTX|PCxu)VDzrb7`;$Is& zs1^N#+#a-AxM!??-4;h~>&!itA>?+<$(eHd@YWo13vHghJyTYHl2bmN^<^Jtdi?b_ zb&l>@^4dI~FJkwYj{GjzscXQ!uz9wI@1-?++*XZ4G@Cve2 zex3Zk?ss#I*W*7bm^hIefa}^e%T(H z^ZTCcHLc$PctbiU%wPZc2=QwL&jq7=XMaxX(?z-SxG!3h zgmV)gT&bLaFOl;WVh;K(1V1IGA+P$r%J9sA9j93{tC@GA5&r6UzmYrEFOH!now%@T zBXeFwIE?eouuFiAwptg82cz5d3?IdN=8s*;E{egs3C520!q0?;HN~1+p0U@J6D?T~ zy><2=j-M^M#o*^h55C`6C7+3Rm5Zl5Jo%^^m)Jte=s3oX3Rl2W#dF0Q!oZ^W$7YCb z^8Fn=Zbyd|oBP*FZNaShzIhUNr6W_pm4eBQW14qF@9uLq_9pKYGnUq`BHr(^3DCFN zhem!kTNC=*OMA&C2zK2Y`gZMyje}^f`FZ+(#nXHT7Jt7nbhn9*xHzlm!kIGM`DEz<8=lMeRNOWiH1oF2EfHNsL9Ub^AaPFhM zAI^C>;Oy)?iSbY7U;pMm`TNJ_h%M$HG*pb?_x|5*=J^u-q2FZZz3Tt1j^_;qe=Duu zjUpcOE$&_AtaqVv@9Iy0YiAZ*WjW#M)4bK+uS=I=(dFPcyPC6Ue!6^zejm%~ceLB@ zu;Gy|zLwn-tAtmPjVSU{fy~tCcbC7+T3g2N=s)pt6Z<4xG^%92v|GhEL2SqhY{-O1 zBlNoLqkH|oxiqR0jrt9<%XQCyk49ex=K3s{KYq%gS&Xs%^d7Q%@g-X3eO&BD>8j8~gAUX1aIup^{@6_@e+=J%Imu8YsQaWzl>jyAEG)4J-p z2&b_j^nqS7cY@Y&CZY}>w2l}Ixi_D7JtDecpE{WN#=*p@7pZZIW+s`+$`(a)7`dRGtvwiY$`>D$AXJWdaYOkM2X}5m7 z`0PFx&tIe;eI5;-i9;CuE?JlTD4$;Tf$Y#Wbe8JD?nM_UCS}(2kQf?pzi=@(v+-L5r*+=R_F=Kj3UfqK?X$A$l*MG9Sn@p z?b&yyne)wW7ul0Xa({Z?Ox<|4D+hvg5U}b#0oflOd=wwS_Dk&UkbH@4@%$KN_$9^);{8n6A|9U0f${`mJBZ*v%2?mg#l>R+l%Nb+$40h)R^zS0}I^p}|V)kLtNmH(Z|FOO6!ATJ{@2e+cTTN%rM$c!I z8<|jznjIHAXRBlUh7E7dR~NBgRDBWqMYQWv#Q9{&FLL_`pPRlNx6ku|E22S&j}g zXY2~>;=!!ltTh$ZX}U+(=riScoWdE_x2OSNYKtol#ah~d4Y%Dr|L>gp>0@7W9 z$Xy#F100>rxLO-Mc+L7Kc&7s|Yi~BZ=L%lgkAinvcX)5{;WP-m;)!4n@V?R$ysPj< z6sxnw*uUcJu731>N3{DK%lvZ$zh;h89GZAF!CGCK+z(9ptQ^dO%bi-_`IkeJMt6?& z6YLev99M>^3xVBYaGR4RWx$Q?lTDLdg4?whJ6Jcfd1#Oy-s8|@K@T)(0VjFca|B1? zyV;r}pS#U*wSg6X^IZE&-W(%ioH?@Qm$~@`fnstAadTWdwMO^bdyT6AUatRIn)gLmM z-Jfvr@zQjE#$H{Y?&q(cF|rLGjq28qhtqw)qJH+%k3M(VV;h*qa`ey&cz+eV--tc7 z8hdOF_Skytu?^_cWnR3pk7FM}v#@*}XrndO@B968Z{tS*3vqGJ_d^%p<36c6f2}tY za|4&HOTY!|h;qdgTTImPoHeDj!T6$^t(Q3~C12F%^A%Np*f^E`U0>AL2+uy5uJ6@~ zFRgR!?Dd=a0ztR7!1cX&ryI#}#2b5WnY|;+KmP+`2eQV#-G`G{f9N|1T{Q&x8H#Q! zqR$fYm_`!M8%rM3IP#c~o&Ctr0d&mUWz>+1QbVpJSo$98fNZ9vx8TF|kI)Zv8UmjT zg-?J}G1fNZxg8ky!c+T@v;D~1fyvzSR~Dh42>n#hPb9V7t)n1YSpRwU*NNJ1p9dDd z{dTvh&z7!FkZu2+I>I|^p0)wcqYu%=fyDX0;PLLKxtqFOpY%`f|3zmItC|%_T~-n) zeV%xjbsF(d@Fcq`3QneggK}^XB&RUST$LYj*>vy&J{8-#o_J^dH0nTrbH%^3U&MKq z?igkJI(4QAqG5)i}zSj|(mHad*zD|5rZU02=j-ce(i+Mp?oy)Cayd?XB%7scm z(=uXkA@+3?2Q~2=XsuYCGhSrN`j9noD&u%_DCb zf7i0+U!z)Az!C?xKXTtwl6iIQy})~l{|Ua=zpOl?^!KxX@l5_H{(X-Nui#jU?>INl z!Q*fjkLvF^@`V$~jG-N}5{K4i{|6qIt$X|MPZ}fO)QJK0-K5-ald2GmbIeHg!Hr{f<-@~QDaY+wm_G>T_w4GB|Ir|TXk zWK(Ob(VLt>(LGMze1ce~``(Qw@ZNR2hb}j|m;I`h;reszYMzz%1sCWDtqabY(7XAq zLf=|29sJE;jaKc(Yb?83@z9!dJoIOCK5TrI_I}+yjef;LUQEC2W+#?(itE1}YH;t9 zIyHBG&y-4Jc3LE5ox;8pG>Jo-7GzLzDcOq_BD?6aa(Kwdu4p9Lg;%t9B-#BId}UvW z?E2_$_#4?j7H{8v9NzY1x0=1$#lcP)EL$CO=Qd#5&s?7HwJOpaj0#Iz+N=L6$;{F5xL=C6@Sbg}jz6;G-m zK5pX4&^`g}P0j`UAIl|o&|7}&i;UsK2^@JURy+}T+CXlgCr`-F!7h2?9Z#Mt?bRVq zOmb;&1&xn@Baguym1A5kIfL1&O>et zZy*Q9;`>{U!xtXk@1QQvvG_j3n)L+ZXYu_?Z_T+pRHeu^_c zypG57zp~v<1QlUf886abA$yO#mkQV_r)uV;c?!P?)Cpxt>@4)`?<%f@3P07lgHtQ zY#wj_d78&#C-C0?@Sexd;^9~R#o^&x^>*(zxhv^kv0!Hw5a7d+{=hs6JShd%F{*xo301%(R+J8=nWlJhbb4m z?`M9GT<*#)dhA&Ap7$x~T}|H1vFHs=$X&9~Axcr(+?-;aE zY|XEqO7g6U-|)@xc>3wA9P;0_$YpGvXJ@;HTfVg;uIwwmj$5zXd+gC7bW8KiTH5@zeA0 z2i!QXIgfu?W}KH;6gd#8M^#f`Zz1nkab6SS<@;)14X#e|&rHk=_!N^?KA|`6wTw#) zi~KeB?pV>?H?Cr~6`3*EjB)SRxRs0>C03U;ZajP3#l*f$PUq+B&wJz6J7-SgGn{dW zp_zJ4a}_5?&ZJ+*l8d9yB_TIX42-@waT+&e&}XuVCuYF7P%xGOBmQPKj8FSvRP6m` z6X%5{7uu&dFsAo`bKqufW*jqP+%ITc?BqD_nOJ5PetztZo#@22QJfbXxUqLr4?yP# zG;hUh-@Pr31OHw@%dW0C?`yTts+M4&L&KWsfMdQhADHFY)Mbsr^Ug zz7W>wie+cbQE|v}&5<>{1Kqj3K)Ic1z8kDLvi^09?T*`OKW>?yp|P{K&_&~fJHLMi zR(&2O8b{o?tLRY&tmIlK?)vjCSSLC0QXkKB#Y^c!Jo8if+sEgwcqy_UUx@54LZ%lZ zpEo0iOOU}je2oU;V9O$@*nDsQy1S0>;_=LuYiw zMCYKRSQA=nkTc~d7E{}?@HTQ3)A>1nm`(go{jk=1Yw+!x(lODDe2hWpgCX=e6up67 zX)i(Vp^x@KkNwc)zzVS9b^m)iFzU{geb8<{ zG(0e#_#gcG4s!BtvYdV*sUPJyPbGilgyVm};*b9|n_Sm){)lHk6!)>7{0s9iIr_{o zTA1nU%Vun-?r3~qYTD;LmJKluUdWDBDMqx0SX_%Q7PnTlAVS?^NM5^I)yM7(LX8IGW}iWf4aqozFs$+<>D zx9m0g?Z>;@bnQIzufK17!{Lj6^VZ*;?s+R?!>8k?arieepEYutrMyJ)HQe@>*qk%Z`%$^|hphn!>>5{K(TqzSUDW6#6B|ki#Zz{ zFo#n;{&Hc>rss!`Wy1i(X;qr8CU| z(FoYfkfW}1h~Bw2be`jXI{PzPue9bo=fd@8A3q(6I`;h))J8ZQ&9d)bg(uzsm$TuE zH|gUobpIaoN|Lp%{r_X{ZNQ_duKnRNGx-!06jT&z5&{CELJNu!YbFVbh!uOGwO711 z9}^6eTIAY$X^S=k0TH7mN81}KZ4*GmL@l|f)zr2GP?2IK!c}`~d&$fsd^B1G!7_g3 z{r&dYduE@LGsy%A|L1+*KhG29%$#%f`q*o&z1G@m?~Spp6Jy=`80$X7SoaT%b)R6Y z>%v%v8kE21X28eS{Cn1ctVTNrAXkvjNT=VjOL`3rWHM)jtd)U0sXk+^%vSpTkjeaW zEV{#U@cv%N7y6a?s};XQ8~_;>wINlA1J>L9>{0MJ=x2v5hPpNp2LL}22P}@my;Z?Aq;Hi7?hL@4)*bFcggXOpr*((B&k1*l@IPye;8o_; z3a)>W<O07oqjXksmgh>?nbV}5rW=M%>w;0T-U zj9Hrms7qvfMHisx2jY}*XU3eqnfGL2BF%dqptxskU?iA=c-aCfz!U?-p!lvAa zH~>75aX^!(M`9cRy6Mxu`-3483tV~KEe=>I;(!{) z0h?mD%DM?nW@2#w+Mvw*9PQD+jl}`5523rDmy4l)OQ3s|@Q17553htjTn&GCHO8KW zs&2xH1J*$QdF}z~62#sEMFz%g@LP=k;Hx$MTXhKoJ(>?t3|kSvAM8xFs+XvMZG(+r zZ2)3}R%7T@W`UzNVDGm+s{NQSG~eJqo;ta)!4L2J;<3Rh#0F}->1#Z272xaffOCIp z#6-^uzw%t^R}OdgD}%__Mb6do@58}cG3NG=fA?8AG3OZjCGc4Hlh2eq>b5_%^p|(` z;x9AzeWL#I4WO&E=!^TyK`mYZ2K8l$W*_ zQRfMH61C=#CqI76g8y8Cn2z=0oNp8N83)Vztg}@0NLks~+X(x{U_a=*wio?*_1rrF z&oSoX+-a_QpYN#lQP0Dek%Y)~J0li1#OPq|oIc=i(823mpX>aA;H`)o;JeFuWE+lih~s47II%llpU`mx zA4BA8wl}Y>7{sfgNA|1oiKz$d*LP5hBJx+@hZp@iBHf63(61r#(5j`5@h}X&@txQ44zGnQKp(}r zf7ViO0lh)c8-QGh_3xLOJJOX*4DHr83K!<)**DLkU0a?WHi`2DVfgfsIgsaK=t}^) zQUU*b0sQlYuoW|vf1WDx^vpfz z4S~n8d@OYQx@rFCH1Oj3s=_?vD4xZ+0C@+BV1FP(9iw4mpmW0igq`6&EzbN;5pw*I zPhgvo*ZS3*W3?~)G2VXS&#S_GIrb70`wP^;j(V^Lb{pzAS*NPkSB~@=&$YoOGMCF5 zJ?2j6`&FQ=2yAGqrW0}%0Y{dXAo?D<##lWB`ZzDf z@yOOD^f5FA_aM{L*k+eM4>1<%I=~m;z7ONXQjSx2<|5>?#9jvSof*?GPRn=q15c6v z#W+QIjQ^h1crWXIzz;wC+~Fg!F9Oa5k~FvkpGfR`zW<%>viToceof5Bi20QG`bp8(1N>1V zBeDNkgc|P%$7b~ZW$3eO+5gbz+*GUoIfiASj%fh$T8??!6ARz)LZ|qH^!N&#{|vntE;nY8co~0out09;|tw4eRFT z7Fn@dJMQ(E3zpB7(oY6`9Pb7H?Q$(%c&(mx%LsP6)kmH!Fy+tk<&ki(ys`U|l z%2l#o>NFp@ul{i>^27)76Z*$uFAmCO8Rn2E|GIztb(v$1>Df5w1ay!(ungys3VP%I zu}$8}9JG8>(&FYHD|@sPdrau|s0jMWT3yyAa^5EbJ$AJ*`z#szDe!#?v_|&%^NO&K zgP7le?QqWD9nxy)9|i4PpvK}Pdp{U!(UTmcTcCeYUt8i#TyLx+QKTrQ&KE{f_ z=dqnJmT!x9du*e8lJ!mo>~7^G%Z{Pl-x)0J2u>IlW<5YbXU*^TgiCSi@7@b!TGk_zh%2(wME+JIs@Z zA&Z5Ge@CD;e;MYbc!q5RG9$($*cR@~NFO=>(<8NE=$P0~f#V3q6kE;=V^;^pgAR;i z*_=k4Wx&l1Kn%wy2+WQ_b5=&!N| zkEABB2N=7Y_P|8@lovJkv0{N`rzbk>!Qmn+p34;b#dMG7Nbkvv>DqUISG!;b6rHet zJO}$5k06d24&H*tZgI>p)cSRcW6n(w$IJi>^oxpPR6KA!_y>E{wA`O-#WCl?&x_X+ z#eHq~DKbZI^L2w4!q-J#Svh)p48!_UJ%-^tjDmYH;CficWMo@DC*ZoqA+S?2M{k2$ zM!1k)D?e|CyU7W64tOYP#;-C5D!BeHS#V(w30IG&xbBKJJc=3!Ug!E`975j2*Sp4f zGZ8q#E;{3YJmLsFuyX8n9Pia}skhO>wmTxi?J|99fR$u*VlC;?hIp}0TVRjpoVqycwr_X$W&0XuJ*m|s zDSxZ4^AG0(4;63Ge_n<0SwA18ARYOx0q{TNeiTQ}>AoH@6K%MyM-=Uexq*0nlOORJ z>zcaxM;L=xJ0sRELGS6S&Ok1-?5l`r5D&55i188Yjmm)w*BLBlK1}Ti0pF+;_3jbK zZaLx}#(qwJo&J>a*RO&s(9fnnMSp!OdVXS9O6!%%D&mKblKLCG<_ebKl zD#U-hh94z-1YUDLB>D(^uN<}Sq0x|i;7L2>XUvB0+^dmmYWW-MZ8#Pk5NjjSFb=1S zTw}M`4sFwqZN)l2!XbV1OVo8c+7)?jw8?Ru@uBc-e3nm=E!W6Bh8i1fPM{{{-@~41 z<9QAJ`(JDja{}=7MO+43a*_E6e$&VJ2j*MytJlLT|2T87-9HY&KE>koY2e2hkO7SK z9k4%K#}>vo94bQ0i+)a~hU#V1|NrG`$VEQhgRBU-03TvCR7Kz!;3Ql-MSlnUB42;V zb(FBVl&KQPR25{Z1U$@wY~&zTV}1zx!9B$FS){*M#(+hL0qGZ0rudtR0gI(f6-${S z9OWa^_oR``vYJ0*>?DBVm-y6?(;`}$iyv>i4&azEzPDa(c)vD zTrZFgn83evo+a;Kx|j!2_BPr1pJBjnJn*Bewn3IcuyyO=^-^M=8jeqmn9rdN(Bs>m zebf5QEx!E&)({FAiO07#y={EVuD8FGbhyR0{-gwXlaGP3(_WKT5#aUl6zC!J znErHl2zUj3No1cru%n?NQlH>YyXjLVVlbsoW2h_8r}@+|=u?Ps9>#86pK!g0cA7f1 z6mepyjC+}f61s)wW4eWF>Q=WHxJdRjd>*zR29E6><0poSk=1>~(}LIXyV9Glc@x-H z_=!&2dYz*`u9E#xujzt;f0VDos1eKe4(`l6pYvZC_Vpk$v6m2RUuc#m>KAq4ndUg) zp?n_Lj!8Vv=R(w><@msB=5SzJJch%+l6!;; z%V}6cgmy+?EK~a~sq=N1Z}8(8+xb5Er~!jKXNz^F*O<4T!Si!4cZA>cwHc3~7LhTH zI6r3%eAB8l^C+IvpII%R39Ugtq3$j8AwD5TZK;TbgFdsR0Z$w^7-pUiR;~(b8Oy!qv^I{zY>=({z7kksK0;XsDD1HXw}Ildo*-EiVNMB*C;zKo}ifgeZlo-Pw< ziYCBDP6V=sd%RlfS0{{TNfMLu`cfO{BIxLdmHa@%u;!-UAJ34!6Hq5 zGR5B5@%<6idX6@ni!J;YjFE1#G#Iipx(|Ii+NDqRagp-|%muIIoRwoOH+{hje`lk8^Ruc)m*9ZHU!;|#T;6nw27Mkfo9Ob+wV!< zHah*Sk@OGa_30uuoC2Q0M~^`E%5lai`>3wmIGmA#n2s_DTTgqn0&9>rU~knIzh8d5 z#FzCJ_?3qovXuv~vS_OuXdE6x{mh8vsH20)OZ>hS`|9N2Jfng9K3Lrk{y^{=x$#oy zGwwH2ADBBY7#`2jw_#pQ@KC`SX&iona4t~q`J+SmUUuwzo`8$z3eFYn{6N4R6T9Cm z;9e5@F7g|d6M*O0J-~J3#|5reqK&hR+2F-zR-PPn?oNEKwqj&F^E=e}h#Y(l^6;!D zsq{n65a;uK+!JlUwmp|4a_;ck(3Xg8AUCnxyW|7wUwH2N7|eBL!8dcl<$bZP#bH+& zchS}|?ixlMU{|N}H`vv!q95XM6n#WBH?kJb^fou*#8Juf-mW7t8)K7aj9tplQ}X=U z+7D})*pC62>bgK>`W%ga-(IEA(LcK{zT2Fm;YnTPquuCChb&(p>ED!R7^~jo+!F4&wf{Hx>z5|kzM}i5pnI*{mj`s#}{KsQC83wcMQY=S6&l_~!FL@$*L6miNa=Pjl74q z(T{UJ=yTY{E%>Eu_p@@VuR1lJDSja@jP~Q}g;f8n!@m0TW&DVZ7*FOP4t{^Jz@^-HBNtb_MfniEbL9} zS@v(6x5vI+4}6?B{?dWtW{IQoJX*ys#_LXbW`73(zb83f?tt@%g!4DqC#wCiVsEVY zcu)Eijmw7O%tDO)^Z~l@1w0u7v?dEOOxW;t+RtxlWB9n)7!X*>v8!&qB-NbIkZj(Vev#%xdiTS3GLiheQnV2t6I zVc~q4xgES#*DbhyBK4s?^U^nwwsFWQO4=?nNt@>%U9-8~TBolCbwSLJUIm;BCC+{w zXWZ*6&VJS!$v$(#`9C$BAJ(pGaIKG(`n8gm5zygVv6rdpAIim55oMDs|Ieupf3EAU zBX7F=*6Q-xF8oH=j2Ado(DwXqAU;k^1J2#-wK(f89zTfs0pu*z_Rp#WkD`m^QI z7A%J?Kt25BqW+A&F67|mw;}tG8BzO=c?0z>a*NOMe$D=_S_5@!{GHEpa^cbC zF27Akz_&sN-+nFmrua4eB+1*eZ}lv1M?2i*ttWm}I^_*^CRx6{`^)Znd%)$lFn$w# zD0NiH+tISPp7tbfJF&N?zF%Fa$Pn{ckr^0UkZ)q!{7Zx0KAvu^gW|ju_X6vHJ>4O7 z_yBacf;v16@`EaJ9N1Nc2%``JL-z~?9aj9Y^Be5hx#0$ zKEoccw#z1uj5VOMdV6<**Ni96A1cQm!Ua4v&&aVz==XM+1Mo8EdZ7uww3U8o4fxpR z_^sOIw|m|Ajhu+8Ri>{0)WQ1*@)T-rT%Ct?vgJ3W7x|rf0`j}dDZiLwOeDWf-d*j& zyU8xUeZ`HR;NesU4-?tT>0-TB+}{mBCMhpUZpF9&8Pms_k&sjR!hDX;b3A$WO~@*I zVEO};Exu1bunlAGv3A^pKX`sN@_(>oO(UnQ z-rbIUTq5vKb3wbBLkYvbDZB*wr{(wI3pQ~b$y?aB&I3PhE9Tuex1Eh^);v@iMrR4~ z8q9~XhC3I2AamC|XFT)Ih;^W=e6Ak;Qz>(LxJQ0+zas9}%sJ*;FL;D42JBR%X*n~R*b&2$OS;yXi z^}-uwr<#v@f_n}C)TVc zPM7BwB#@1JO6|7&9<->huGdx*b|{x|v`J`(xEcs>kyzx75--^mZlAGPG& ziF<(y?pJHL(&qvN)k5{RW#DRY)z~eFW(F!@nY`_>Z7rHBS7W&J$hC0l- z+?R;IroUWgui9t_Esx%8jSE3VO9CA|!-bYijh0x>1U8>^o$u%i+WMX74?zR-fyh<2 z_=?}+ckZ*VawPnXexLfs`ySrsoCILEcf7Pyj;q@dN$jUcTjNaH2T;Md9V6O_0KACc6B`7SNVPR{T$$-;(Wdz z03IR-g0W*Lae%#{{pGX0#rUVe?lZ>U&`XTJ){60KUcvcnjDxpA-&73j#rta9Vyyoj zaz`T02YfBYZ(@x91lCtLWBd-_TaPw^=(jHt=YzkAVrJldnYj(}gg9SZZ^5-5=NEvU z*gen}`p6#V>ome{+2VcDS_1qjcQO1S+b1#R7dStp;lEnDegM~&-dg&lUsD#Zk}<3S z9J7HVW#gKCO4p?y3*A$(`9F%|}~h>lJU3?Fqc3^m484Pb&JjC z?}eq?f+H+rFSse?|A zFN(gC(VxfmxcBE|)t@qM)#*Et{(P|)I;T14bnDMd)X4S}uQE1eT-pxbleL*?kgsY+ND1d z!ZWUNt>RX^+m3e`2X9k6*>2Zo5eHyg)Z@yoe~s%mao`lMl=Pjr$ua9!zB0++x8mlU~i2*tg6Y~?n@Z6Y^wOJX~%VmS}-pDAVe7Sw71 z=M>P;jyR{67!Lm3WOFX|EMyEoxW{oz z)cHX#!?>?vxCrnIpk0m|evWO>hgd8J{pl9V6F_qD@g(kG2XyAM4Trpp|Dd zD>_9ShxV9vdKonw3F5dK#&L^JJ6ii=oL3NHJn7}!#)al-k0NFRO=0wtbNm_Z=u3|6 z)II)-*=NW6lJYTo@<+`#(0}J*P98K}C1WrHydp0xA3?@pkS+S%(SNb*Lc|6BPRD#8 z^?|am1~yT}RkVpJwqjmH#Z@BCLTq&h@zL~wu@&1;F%@wOD%@0DrOQ3+81dXK@lowf<`P`i%9_wyzB4Un<^IM$T@-@DSI(^+~JK*k@GDIHg{QbY*|KH6I>Zsm_jOA!wys(+H z!TLS+yRu)ag#R2r4}{P2T)qvMYfeKO#YSr9I@SQe-=ZyME}!dyD0}6wx66$*bHm3x zi_e2|RA5(O=jl7GN1Yq$acj6n@G;bSKY>~a)_K45{lIb7-){YW?(tOAZgZ_1pJRrR zSbHKL7`GS9?IG{^oT_Wh7W)(rFmFV?iQ;o9awGI>QxL;k1NwQL3!eMXAFQnu`Bsbv z3xF5*Br8FiHroc9XZtS40`|#G8ou54!2+V2=Cs7>h~_>_H4%xsD=)GmA&?9?mWHJ8UUfu|5c6szgA=auZOY! zS1tDcT7&(+A}M*td^k%vU|&=4L)4ja-9fkxKFVGr+V%`!Jdg9nc4N%W#d<{2@2gA- z-&)`copQ$)eqadxR=5cGP6NI(fG>Pf?tOItKIy?Zz_%Fq27qq`@GbIL_(Ct&XlGd5 zr0`u}$9JT}_dL~K%j0|%eKF8jQ~+P96ghbEp7B=fEb4JsYcRebIvIIPF<(%H{jgSg z@*>rqJo>!twbwSZpPSOqJ{f)>N2vBujV&IyGq(r*iG_vc|h zwmj^`mdAa(EL}+JUds>OwS#xmi~HulRw3?GHT{uU!1WgRe^RHFK2OhLY|uTw7(u^; z(LYX|Nb!SC*xn%M%;wq`@Ic5pd~qTFQqEftS5waGrJQ@f>zJJ9;~n^+ZSaHnP8MoZ zm!60F_&sN&^*j0a%nNq8|DZN|*M4_il^d^OAm< z*B0%;o-Bnti8drGwxM8=Rw3V`J$YWh64z|Aq~|vM=quW$5aLGBXX9X-oV=I~{}{H+ z$&2eGFTN#d%W>gB4%P$+9-!Tu6c1$I#_$JiK`BEe$T<@q>QhXHC=Zx}QT)ZTlU2TD zl*9r0n@DbIq}(7Tv*adt+>#s47ZgKIz5`gJAVVuCLj(LmUUJdi7Tn9h7&uDy;f45( z&uxLeli%0M--q#8$ktZyvcq?ecz+0f3q!WT=zE3h$A7eBocX=pWTzDEE7?ifPE@() zfoLNtZ;ksiD*yAbtkdPZYKt%KqbhqbR?Lsb=X;=oHKp%d7v*QkzUzb?%!UklFNw>l z@(sM4HLv`KTkn#}c%|nzy2>{i6MH?6XCiDf~Y85C_m!2zE7$ zwc=K-UkYmdvP7+42=C=&10J3uKEh@eS-c?ae$c+l#lM-3e%I?aL-3KZvkkMg_CuS` z#5TsGjU!Hbn&ABwm-n-@_p^QrTZwl)%RO)8mm7-?p|3qD*7Yi0x8N)e=*0E7X1}xz z2#UD01?NZbyXqfje9rky#^3D&@HqrFeUO1YbU0?=>>rL<;9))ZqijY9?YALb3=P3} ziX4*ecL|;jbMQ1le9k;0d9?qZyUQ4BSN>b$z3x9Bg1+eUw~Q-BlmwgW#XRCf)H3@T zmm0_?VZ9FhAjTZ@(OUn>z7GTCm%fpXe_J}}CLe%Ti-s5Ljn(tie;Yp?{TWnqn96rX zy{oMIso{C;5B=00>#MvgVXoI;Dt%M&u~Xl4e7NSL9kCAaBCq(5{e8H~@^7c#1pl_m z?%&?&h3_J8%QD;6qtB7=FoKiKwvFJydTTD5xsfvDD7gPp(crpAsl$OOY0(^9ufd)p z++%(_VwP{9c7(AM@6XM@PMjAd^gcf=+5s3FupdGPe9AP`KD~my`}fzuk6UBG*asNX zWZM-*Y0+0?+ZAYg2im>{Z8QF2dll2Ji;hMg@%r{@Y0>?xB@T`@_dl$)U5mCKc4>P? z_qIptZO_!&o~hct*xX;OwGAJ6f3-{7v%0rESa16lt?gS>+n1U9tF*RPqU|b|w&!$j zJBk{B7*C3|wu@EUBh39vw6-hJ_7a!21KryW>upzPZC9wabItvCX>Bh?+jqINy`X#B zyY#jfYHcr6ZC_~aUm)84;FXqv=Kh6f8-9Bha&&60PFXOQ)#$|2q;^^Le-t=%Nn z<49bZ<_#!RxHQchglpI@zEd&4YrQ*2eAZN8?@84L>6xduF+AQz#c=sN_NdgKzfFIB zoc8=U`TXd(*6o}VF~-%@DN6Y$3HA}_Ae-nxzEJyc*!%C zJkEXF;@j`BKN*ohkcHMy~Pgu zB@K4q#p}d=3dFJG#k6Py`W3(!5}AdZ5XO)I#*hf(*jbjIRYG^mF-|SVIJH9R@Z9RO zXh7<)qVGw-U|xys&Rrd+FR(g|xu(_t&ajEhKukLmZO@3e9c%O9?rr|3-e#@VW-aVa z*EtPcTa4&-*qs3E4rAsH>>1WE4WC6;`&)qfy%Oi!*Q7-^NSqaqeW-57SH8y5k&-%ZG>CIUIR|Ow89j|}>Gku((0oG?y@BaMZk=nN5^l9(e%sqQU@DsyZ3{yRK zEAp^e*fcycpiSGqkF+r`jMxtiApk>uhk%vE?0%56rC~?<=lLihY)h`{J>8={Jwo zUhjzis8i1PhU4<#?_@iwO{1|KI;;$0xA-4jKH`||w{6b!V;rTiL(zzCRsMz1hpR2(i$g z68t>yjW|CO#rvxq@xB**nTtR0p)-g5I~erfkMyhs-*eCh2L67&v-keop}}u=^kwF5`Tif*7qpBNAbOLE%Ko;{(^U6PblOoD1YsL z1b>k)7yJeP1b^{~@vk$4HB+USPg;h*KjXQQOzS!B8`gn-cpRVm*ne1?#y$BS!(ScV z%i-SB+5YG{{JqBZkvnGlL_~d&bH@9g2&S0~^3!i9z0mhu!Wq!1ezhbtJ?~h&YkKT?y0Uv$* z*vC1=|t44)$+w9Lf!4Kjla$*@6OM!nOK8sOL;<~?wBTvS$ zK1;)oYdDzKQU8>DW;*6ueZYbTN&QBr3-Y1t zl(Qdbt7x-+l>}XLHM(Axa<1gtst4;OQlEV_KJ1%0}F=kPmlrhF@Xh|v$+Q#AQr zuG0m)1zq@b(p3suy9|HdPlB%T8eIwNK~7K~zNO>q=r5(mVmt=z?7Lw};Oo=yO{l+O zekWd^Q!@l}|7pf^2F`a*h22>PTpowszJfXIdVDX&8i_L48LrLiK&;347+I^+#9E!- z97e4U>T|^YzKAuqqfUoDq^i^TJ?b~mkI@uar{h7L&f};HS%*3uKBww*p2c(YFV_Ml zeam`PH-mZr`bG!;iZ};(L_Np9V^-W=y$ZQI(~CXzA|A8OduHd-XxHqcxrRB^Yl!t# zF?ob8=<`3MzZL6qbC)y;=E2&z=UliDD2JcCL+Z+dOtUuLp3w;{rQ6hh? z0kZh{pm_Zi@oU1q1dtb$0H}3lR^8Ja4G)gTQzZQN6x@Ktlbp}vaQ}{b2L35|=?lDk z2fVC)hL`Jr=j&&9=?-`Y{>jq6{^DonU*bB?{@c~h@OC@kZ8`yXJ6+_n74k_v_CP+f z9WuJv0rMdV^IeCGdNdg&zfVLyYaOr_p8$P*(T61tcysz0Ua&XLzF> z@Y4Gk-T()@&ad~czQ6yDUEg>2GrYYHc+d1RyyqS8YWo@98V9^3{S2?t0k629;RPJ< ziuxJeGzYxVCjig#_w4@5VE9+aHHh&cAM;5e>;Zsr!rG^`$ns^zIACW;*xWx(_)yZ9 z*{b<>Zu|Z91fPCg3I0mq|Cb>SuxhF-_!$oP2XD4)L0{qT6#k`ye$KBn9Hr@oAD+cntqPu5Gg{pAmB zbiiBL&+w`p@D}tlyoC;UGy56dEC;-a{S0ri1Kzp)3~#st-X}Brcl_@9pSUhkANKV# zyodwd_I`%9!vSyO3BVicV*e(<{xhy-e1LU}HDy@0$UJi!#sS?1Z*;(4DdBT{p>E^W zYGZ(Nf6+rO@yQ2>Pui^bBoE`yzd7Nqa=@K0;c|S@;f6H0j9(Z>tA8q9S>-}M^BqAe zuaHMu&c90F!EvzC$oe;)U($xTRGk;)4xFY+oMQP7IUe?w@93Q$S>5mj#U0f~-u`n@?}z&x!wa!4$7txl zJa-4$Y0LHJg+^mt&N!^gfgjP1T$+$SInVu)gPzAEJzSqGbRyTvLl%4z&n-FEuW|mn z!6o0;1pcf1pQ!Cd-j()2&$&e~M>Q^BuZgbHaAF;_s!dk4+Djyk!Y=|nYCnj-Dx75> z2y^cAYh!jr^6)MV zUyc{EB!0|~>O9QR?24+VR`^)<#Ey@$C*FbnhLoWN4QUq?HiTCj4ehm_hW&Nk22}%~ z_vg(HTqa0dV)jJ!r-qAL{&ba#eXW6g4Z^MbJS&6HCn8(;u-NqV+a1Y*5d%~agV%NyCjZ9R?ffkZM)6bC2?duzr`a< z$JT1{N`GIM*PiUF!83&?dCXGOSL%K9M+e-eCEODk?;2d>LFKERW7;MM+|?3pU*ZW% z9vtzNl!rN}-6@%feZ^SAgY{K9%{30#cRJ-EkeobN{I<*Eoq*4M+r|DyS%~E@pQY9b z2^^$Ms<|CCmoZcIH{&?iE9#r*cPX2tnru4PPb>Ri@!yUQ`A=VqYizl$Guv?ACu6*m z{}OIb^94!KFXk<+{p)(7f6%F*AGjo<|NR>s@xsZXAM>)w^#5)NUyWD&(*M9E5&fI` zi+;>MCqw`0Q$as)Nksp`{-PiCP07$d^Hk6eToTbgzQ5?tPLBRTr-FXql8FBIXY`Z) zk4%pK-4cGk>OXKvME|D#qJKm5xPX+zJB@zAO`-}dYlcRsosh}UYB%=TQ>-$OnXD3Jh zZVA6%^&hw-qJL9=(f_UF=wE#*=m#!|=wH}h^xu{o{WDJm{lFy={p0(K{*vVAA9O0{ z2QG=|e}7s(>3?~0^zWAN`&IveOCtI=^%wo~lB0k1sh}UYB%*&|f6@P)Zk{`oHT40mEaSgqTO?N^7IhSa+)K5);sFzOlV)jZsGYmVABA2`GiXIrpNV7h}Z zs*Zv6B4U0pV$D^SYV*R*_5Nyp(3-EauXo~nrK-PRO)TdL1HiSUC;Wg{t^=>we3il{ ziTTPt@h^B{{EJ>|>9x|UQ^G&s+8h7&I`BFX{v|d4x;#1k+mr|&nFl>7^UuH~k^Zfg z@K1XF^}gijUwA6$2QG=|pLqiGTl4+)`iFSEh1?sY$g(f9cH8SqCOY8-EIZQEeE&|D z`i@q)-i!Nru;xXtv$(gGJ`1fD9|Lir| zpI{FU-448S(&5={>plm(E&U8{y93_(eulTv0dM6=hiC7{}wvn%{&2k zJ4O6quSac#Ki^`lFA(QYiE(Qn_973anPJq0ci?(MeVVx+`%HiHeYp>~tUQ|AS)ta|}_OLle5 z=I_|I?V()H@#jmhuL^46JBz@#T*J4k6wl-Ax?R3ctL`r}eAV3RT(1ee0ySU_4jNWT z8n~XoRu8MKHAuAoOHcA>U>`Q!&!~35T_EB1WxVgH9{I&wqqQV*U6h}t2d=Z#BY(#M z`$h@7&+AS5D4#bu;9k~8dFiV@e8mCptUkh9h*PXazW zUG(E-`UBvNTYP>~0$eU};KKC=!uCp^d8s!3>*qa=b;*Z*0Qh=7G-aU5<5_1v27Agg zA`eR0&2Zqu9NvlSr)2TR-e2So^PjQYC-Cbnch~N>+mRT5B32G15r3@nOzi&JV2rOV zSU=R&eIDwq5=@OW6^agx`&FkeGIg?a2k;Nw^F<-miy_%{uQSiWYx)rTQX z#uKgYwdBdp4~|EQAKqa-=N^f}*EJksNydb9?i?{@giXT;X~0DiU${e(MF z!i|jw5@t{Nzv(XeJ=kTwBjtj)9PoVCpu%Chi!4YTAEfa?ohK99&mv;=$yRNS!)-r{ zLp_WSoQsL+IPih|f|Wqx7kTpMv=yb?!iPjeYOC z4(tu9d>QuJ%Mz~>!N=l{dww%f{_SN4{0$O*OkV=Y>r1BKhh1K&F9!Ib!>@T<@}SC+ zm&Y79ESEU+RbQ<2-;VXOkYBF<*5OaO^jNK0OWg+f48gvIv2MC84K~)mxlO=p9CFPe ztmlN?Z8iJ@ExYTp?C$p+_`8(aUKE7wbF^6a+$`;g$59be8bhHw@^7;}pC;2Za` zRO`Rn#M!bQtT*){7jK;<*0IrkZv59Z+;o3vyu?w>lf~v2C9cWLFOGGQ_X+6VY?+S` zF=Pg9gRk)u)yGo*#%Xv_&Id?*V*5QrEIC`N*+llgZG1Wj{Mzc^SE`1W&acQdcKdK5 z{OU;`mA}SwD0II*^s3z+Y?XMNh(225hut2qzv`vGsKec^!PWV&*1?BZe+7E)FKa!u zUB1)t18ordL#R^a5^GLbw zX+3pcKE7loi6d3%3n^wJ|iVVrrPpEVeB`e`vTh}4$01Meo=kEcOZIrBcCz{3gwhK8#n9$L#uDso_BQ#S(sB z^xN`B?D1eL;z8~|9i9xjroiUmtRw^eNe1j;ChXy9V*Jd&UJ@zTOCnW_^&O(7=rrs# z!F`G|8*fy)Rb=@ivo%~}G7OvA4x8Emn@ar0Yy4CGH_x`@9x*)c!ixW8{$Gm|-^4u? zAG-D5%@|Ld{Wl>2KE&g67d&F|wH#9t#n*kYKc6`IbAW~u@#wtT((k_X=b@9}fBduR zJD=5e9k0Z7KGt`@Z@a|ri}-8vUF82x^p_r=cp+~pMq%FJ%^~*vuYafEN51_+;&rOx zlV3RC-y`9lh(EN-rT4SJQ zpH`nwJ5hXo?!afZ3qBK10H2=5E9I~1KI0V{T*}5d5@xdYxv%ifcfk92ilxKJ!pn5o z-{)r7W1joww8l|HLF#g6U%JkD?F_IIgffB#3bzhhnWxe4;C@~=~YFVCZ9Od< z7u)+jwehMAcDtqUQG2NouS^Xur% z&-Zp7%|EV+`|ED=#fP`2zLD=8+_`f(&Y#|%{>Cpp&Tpvm8Ybm7lv@xD8OC#%yR9kR zTNn)^-YjgHVuo`)(a>m5)JWsGjAP7g{|*0md;#o10nRBeKzzO^;5-004=TWUUdx*I zk6zY%VBE6igX5Pq9~xc|{aaQ+v=ex~3p_8r_4C@cEx>y_Xx#z*+XY?U4PD=BMEAc9 zoqPv-749>lhayJwZwHKMXQvT;_kG~@p%Fcnn_?b~q?&6CWAJD1U(@hez<47C^`q<$ z@;CG_#^9CMuc6vHSMJsSFlI-l72s?rPv_QuH)g*F9<|LfqFWI=gk}OZ`Xe$2cwnA1 z;4_A@pCV|3&ma7@F}odYcHo(1s8c}xY**mjyY~h@U4DP4&L3TmS_Ph6ci((GH>&{W z^LRR!&cnH1Xq#=aeYX2P_#G(*Z=STys9Jjgp2t147aY$QjKHR$4#I4QEekIUHn+_J z4d|bIoOyIH&c}@4-F%!ERCYejp@EHzZ1hJ{2L$(Aba(2S{WEYrD{!e8xis(K7~_qM zFZuUor5cOIpl%}qS*jRlym9bu|HD}xU#EANG5a9o>cHJUdN_Ul#uug;>D7mDFCX`| z5f12A_qqUY+u8UH^v@fdUY(H&J4pKHRUD60ppO;+j|$AE0B`J7dU!O>uRVbCYY*aF zRi0aWCFnYEfq!q;mBu2_)(w|sXC=m^@XYSG6b?3K9|A5HpP62LgT!U@{EFiT7D7($ zg4`^$aOnarV}Z-qoJ-9yXVyI$0WJ!+A;2lTz-TTT=`n@g&MAtv_?Rd9viSn`e`7qw z{1W*eLZ7fd$mjht{87~g`z5>}*nCLzNoMB=oQW4+Xfz)}pX@X;tHTA61Z%1p{w~5+hoOY0TD+yb*zbzefLw^2-_FpkhgUn@iZo?TyhvpPS zPdhufXWZSVt@(~8v--YFOV@eE_7kuZ^{AtuO}X!Mzc^pJ>U1NT3%&$eE;p;rj6a)! zb7y~nXL%m`eP>(GUNQjJ=iz#g5xw_pBl^I3&`tRH7tJ5{!a?AYZ)9{9Vw{V>KC~?; zz&XX>74RwZq*UXKpw^t0vrW#W_z!g<7Y)7goo?kTx2KD*3-$UGQ_5xXu*UXNzl#&u3}Ri25=31myof?U_t*O}M_sG+fu? zd}f|+6k<)B|046BffwbPdl3H(XAn+>+|M2CtuCKeetc?I*Rvadd;4P8tR=8r(EVcQ zY2o$M>w;+N*x;V)pqB?0`=hTKDb>0>UJQA>f8IsM+aZ&*{}t$~jp(b(!GGHHbjbDS zblCM&v@WXeQ1%04xg3lG1 zW7(z`Fs6I1hOVG*IiHvxG!B=@^Y=ubr-PRt3_yM1lFf&n!kLdaq?l(5UqE?4dm|>` zUFedZ?_O;jUJH5RTwMFR5AJDy@2MlppMLGgOR0Z9QVCkOLXUVZ<m^3r$8;$$MeLqQ#couZGMLN3EH=kU1`G4kiNs^kz1_uY=4H@30~7a@VWBc z*F{G{K6oA2jq|Sa$uEn3_VbQ05AIp{^iwu`3qI1mp15(=1*S_l*xcePrdkJCCIQ`0s+( zYsp*4)0p=j-1F)kzdAyG>pWgJx_6v8|0sAIamU&3#2NK{F&-bV<6I1!59|b>EG zhlXcPCwK}xF9M#g0M9xjZNbvN#d%veqIW!hsPOD`$MYN~p6_dTzHi6#a`G1PT)H29 zPdRRQV9&}kAjc2B2HCK9yR-xMfb+`B9^7-_k57}|3qtLnV}d0cOL47aV+8b~H(9w|svKKO6gtcu15 z_Dp*nc;6And+YyLd`}17qk%X1o`c_(1MgRWH)Ut-pW?hnY|$&;otWnkGX9A>-YcDW zcWHQc+3|iHc<1}Ud+=AcfjcgK?a0cfrC#m@zOZGFzq@Kr+K+#AON*9rwk=eKy9Z*Sp})r8KThH=1%D8E3A;neJbC z5^*WUHr-|}!1MgB(;L1u3f~Xi@x8=}?>{tr|6#{>2H@lT$~Rv5L)Y^w=iPo~ zm#o8{h2Q^xc6Q?LRs8M1-z)fg8GkR~Z#(|B;jayOsz2d#3;vX^HuNs~YOEnhX>1nH zSl7SSuAkAan{aLOtBi|GAKLhhc*fV*g3tD8INK9rDEUOcLXFGxyBZ(v5`GtbF8W@= z=c0$6#C>1%LQe_)zqRG3G~6&l>Fj z++v&|9fltgo@T`TC_G2_%K;yL6nx=<@PSeq=?@2wcj0VJ%lB>hH^tA?#vJ~=Rur3FE zuG7Z)6Utj5{Yi{z<&ZV{dxh|=O6HXv|NLc{pNsLr+ZbgXiqwUV#C00N6ZRZ59UI4(2uhf-%x`YJ~xPGAxA^}c-D_+ z5qoF(@vJ|(8Dp4%e=46gHUa;9;NM*EZv*)Egy5gAvrzJH1NfIZe*^fJT750)_NR3&?9T?oZU2Qj2m0ozki${1KR(zT3+_c`1orQxfkxBv z;8kY(O2|_c<-~%kV1*zv0>0rj&*}r*hkJsHST_b2cDQ{>4{+NCm`9QKOqSk%?+Gq) zJKI5T0Q5!{gU@%x`P_|Oe}?&1AKho&o3MXNJm#Z)fIGY=xN|f<7lY3jkCi;@d|t;qcYVHjcOT$>hWUGUK3{A`W@+@^ z0(xilKyTYX^I!V_cVAC%XK3`!1idrd=@oiZGQjlr0q)j>a4$1Cw-wVP_zmsIsU+-Q z!)K1@1KjmJ!JVSfn-6-Y^hB@EOz8vM6$#;vFge#0)8EN}JFy42hR58$H+j2Jkq~aK z$#a8ZaK{7gICr>0o=fu0SNi~WdP2Atnvq-qw>1F2FESc%F=o5zk&vrw!))vW+;J{& zyDHCW=vsp@#A|#IFur6)h683*ib+2z0>2?4?6xz^Y8TkcF&3q0V-nA?FE@5C76 ztwDdWeoM`39~_Ojbm&HGemi2(nP`67s=sj1hxzRgzkUa9$?|ush#&3!d#9A&Lm0b~l3}j-I!Q8ohJ%itc)S~a zy{JKuGF+rQh{v3im`FxOrUMbIFeGp|D^d!Uc$XD#K?}^mAH6RYt@!@&E z$W4gzfWwFl1pg6V_*wHF{EE51191sr!=w03pBLYPx+>0#3%k6VxeDX(bkHj7@-EDk z4>6*n-|DIjL1r(-r_IJ7-eC-rk2X{cOMCL9q))H!oGRTX zFWAn{WIOtKZY{pzw^ZNoQ}vIgPF(}sFHj(tbo>=JR>QG5ms z%w56Wg<$XaUBv%>ugNh>(Cf2mggmI#$U)r%atMuFhw{&kS~;tZ&1n1tbuT(UDqQ&C ztiSo#Fn-boyu^AT@Eqe>s}USxUXAtW#3Ky;M*z2-`*0vn)COF2+{mx50S@(7=T8gr zNL*X#fqZ&lZ;3Z~sA}$5W2E!69?yHSQD=b9Y$MpLo>k9?_C}(;F=+3S-rEz;Pr&n& z@cflYK9BiAj~~xZ?frRs|Ht(+kv?Q|9aeCpIU<2RbfNE*K9oX!@>7h%lwDmP0+12v zLr!+8IS(A3)@j!RH(2Jz>2l|mn zf0Vo)67~|nL%?y0H@4oHurTzGB;JYUi^XzT8X3qMfwNp4>yXLwmKTGSZ z&$9J$Pmn6h_oFSi0d(v2z?s7S#@B1XNB@bC+oJe8U&T8|HC~Yi#P@KORo}J>b3sR| z?DcKXi`LTWYoi5&jOVtZMkWXOzR+SLS}+Rp`o?54v;_UM*dJXAev5n{@>!@~8>;M& z_j}~2b;jS(h|gBDHAL!STP514GAx>_CC#g?&%ndiMMEngBh?r)R^vHeHqZz6brA@NPEV<@d_!H2M7H?$3h<>-FcKl+Q;JJP&@< z>d(KJW{opdR!xqO8F@`PYnpJhcCjWiO1>L}yv6Nt+Z3bONTuzaV6Md80--6vW{+Vs z+id3mYi?i-+H4yTZ07p%SbIWW;_ZP~AC&Ej`zxqhZRT$!?-WEosqCr^V=iH7i?pNL zEg4ti^AOZ?Qs3Gnyh;gA*O%!I{nX(dLj0&;wE>pJ6FZEL!9&6zk1EiIeo`2*q91*$bX?#r@2Ni6EOEEZ55dlp zU&GlS`rHu4+SjFwDS9mX8K>v6OuPTo=4C$p0uyBtwWZkm0hBhr9K{S zx1SdO;_{cszX*zY-fPU$BJTcnJLsl=k>$5|TlRijhDD!uT0Zv;do3U1W_;RoMEcxi z8vn?%*MTp2#`RXT+v=a<7kwCg{w(Hy2Zx!tTiyMQX4&S6`WpezX7j&6oANcz0$ubq zhT=1*`5OG5$k!;K|1I_kvi$EK{Fl?$u;P*74&JPkbm;!qDxrUI`T1prU4E4ReH-M* zE;EV!?>hj;>3^4S+=IWNd<>_4w%~qW{0s4XAN_AH{O`W_J6`ze$v$5X{5|mNOYxZv zf67UxxDS6m(S6e6g|DA1JSESol8=8e=A3U{6t?s+>u#eV>uwMJy!i7qWMx}=SPEIS z`T9~1=k1C6`gh>ku8aQ7!7J^t-n*AsddPgBfqx3$1p0>(^#Q67a)VuV>-+utGbbr5|@(KM*B!A*Q{QDm7!}ss;KH`Ez_f`KV z7(Z%Qa~an1H6>}_yD`lCVaR>__$GKO#*YcoU!>e-#A4LeQYp8;c|*#r|1@(8uI+ML zxp~iIyWCzQ`>2oO2gcZ|%!jwQj~`=ChWwH?U4B828bg+XF3N8eK6Ux!_eAoW1ODa& zS!-jBAqO`(<+pNkwSzbRxYW{VU4C~utov5}$harozS4&Vcl+H~r^L5JOmcFu1!OTn@PqdCTBX^D121^uYI{&0ll$d-Elh-Q)hbihoLf6Y2xT zu0aX(VS{Y%3+V&t()9tfD1G>Bx1|qV_zY_L!0(ClAt%*79+dyeIUf9IbGm~scS$;W z8V~MIb=QaMvCcdMUiU>GZUH^b$R3Wgz_f*42G8Uw40F5&A~R|F<;x zAJ$#||NOd?|CS5QeYn=;zb(CT^AXgy=ws}^%0BM9{D0Ma=C|(hKV7!@h3qY9)8!xZ zDEY@a2OOlPxBa!d{{r8oN#rapi0M<4if7XxQ(`>9JlFtS+w^IG zfAfV7nYkZ*$b4v`^#lq07sTFE%zZDq>(jr>HcwEW;4Ez`D_dF;zo=kemKAyCJH*cdhMz=LZl8&DIms@?^#*-T6V}?n>lx?evfD>63h|20V9vrMr9ydkcRh z+4+3&d>`V)62_mfle- z4}SF3C#a**^`XM0{}bwiakaUn*}eRvwQP#-qoQ`ZN6PoxjX zr(;g`%$Pn5b?5{6@w|f{xi0*0<`b_>exej?+J-WMnO{ibNDdvF}-1X}o+0GZzFVdsy z7iduW_2wTe{dyapx_o^`LPf6GP=E);ldB6ewB5vHx~YVHuS5s zkNPzp@YHzofAtIF?Fs1DAxAwJ`!jbG)(w}$>p`>e-N1L$gTeb1?7e36kI-Y<#s!^SwM$sb_%Lcg(dW#eY(tH}<)|-W&8WCn zYt@YMy0kV`;JPGmg{T4LJE~@MIOPMp8G(0MGs^Fk*zbv-O9GSKp9i1L(VxejK;rpG zg6F}r)AZ-D4~($c32H`jAy?mZ)W@uX&9tp&A>J->{o5s7Lgq`c-wkmp!@f$SmwQ*D zW_l9qZxo%Sc%N$z`CJM1)X~Qg*4KD|N1Ka$bH52!eAZYp!MbY!8+F%@D17Q9KI`rH zysG1a_hb01>5k7|MQ~iw#$Dn$;7D5T!u=3vLk`UOjDSUazb&2-dFN)O zk0q$-kDx8?I(9^vHj53TkBCh+Md#SJ@^IW zg!*yzuWmEWbl`A!q=mzYtlzWjOT7M6tY?8Mv0@&kA+65huj+GVBD0rP$dGq!)bl8?{h`*G{*3!MHJ;e7!4pzp5B zEBz*Zrw@Sji>--j^FKxn%L)fhb0to(^Cu-vA#D$A$}`-o*gvID4PyVExcr844Y9`d zTZogFV%=>B`v-)W!-CAzi~XJR%u6Z9taAe`5v=_z%fLGMf&STj`V%#QTds}W^MZd{X`8@P@}7Huuf@3eENU7! zX0WD}_L%#AJMmxpY0+K}_Viwx;eW`3{kq#QZs_!IPpW^?cufBOKG(8kPiefR5GwvQ z407-nHL=B58~HB$Hu$E`k;mfPE%2MXy%Ds9uqUqg9lsS~4-59eCE%gXk23JXN#hdG zI6|Y*3mSF&$(spizZkWle!wqAjRtfe4r_4ouu77Ebv9tpU$_S`Gy8;Vg`GIx)Jr?` zAx3}?2KG_Q2L3udO1?&bH*Y^Z+~0xy#%Q;QJ7tOc zap?RKdL;F!9P(qWDWaW`=iO;~%>A;hGY#GDr!_tSTI(G2elo(+g_kvYcVaI|onFc{ z?Fi2#W}i@>;bRUhgslLL!dBFQM%s#H(1Y9Ol}lT(?NRJgVzU*bb3gv1t!NEcw6)$2 z+LWE5tq^p>R`Bz7^sTy=0{+34Kexhn{Q32;)kWUp&!+&tI_z~qS;F(J(*Cqk-<XdG)$w!_4K^rLz2Sc-VE7*{NxAk9 z-21XS&A!HCsAJLfs#MavL!(*g*Z&vjZFbN*swa9K#mX=?swSVvaJ@}Fjy zh~q@8Ur&GCm|(4?mG~5+UP6Dr1n;j+vEJXx{d?~|y~p=Qr1+Z)K|gGNvl@q-^wNjb z&&3Hy+Ud)xb8)uGKIF40SNk7Iz5C|9gCN_7d?^=Sd`8WlzZp$Orr(pfX6$DR_jdYH zFJ5rZX=@hX`jXG<_AECuhtK`#z~Qtx5$uyG_Jo3;lJ@Imdy26qGX35PjIU!1?1PF= zFMc16whu_VcMzWlI)oq0efNX@RIv}jSi_$;7H2hF>`AR2m%{4-xIV3VT!wZ%(4SY1 zb0q?UjW-VYe7nYeR{8K)&yOBjfioy_Jf3Qt$MFXHtr2lK*GA^fE4i{9W0V1%U4d~v z3qIij?5Y2Xq(3H)(0M%{$ashQ>`*RY2f*u;#^<`aYTF@$LGpey;vdjKyU-i|eJ=dx zdEwL#18wX2qVU-%^do*ARcH#%MuMN2hUY60L#=}h?!SdH!1E$ffd|h_`qAcc=+1KJ z&ToGIVeS4~{LLNWK7AW+<4cg6$HAYG^DcTpf6ua6?&nfbclKsmdauh*xAlgwTb~b# zpAX6NAbBoi2Rf1Yl24pTkW7@DdHi-QB5j_EkkVyV~iDu zV;I9OL!1^;Gov%^S$N3 zrv+zS@Sg4y!k!Nejg$Qkn5B|m%-24KxbqyzuX5ne^9jlrS32kj;k^;ySvGi9i05+s za%`ApG-o01_Je1kY5wLc!?OTm3jNX#O7D{X1OM_|6!k9&`~mr;JiQqgmbc>^4m{X)R**0r979uV|;Zu6sSsUnO4Uz^@Md zQv&&59ybI0W(`^lXY|dN&^JEAzlZyDMbJlHyw7JCuj@9=0~u#tBL{!Hmkqv#@ea@N zqKtDcWIg(<0ldor?~2$L=(7U!nICQHZIN!$`g=+1YcBL1uJTytn|*~eo)y=@C6Ix| z*r&Q0_GBg6se;@=7sHiCG!3|=15e_^{n8I&zx4gEK|D*3d{?%oh5IEhhJK;%!j%A*R=q=EVAASO~WVc8Q}AL zun=H|W+8^hx{FW+zT;Waz?{%l^s|B+fNhOw3+!eu`sZ zYUAZ!IaW(JPTSIj`T?homq1S8(`mY6bb2rbQ(pqm@et&m?cQ8_q&C}Y*|Rbkw=u`o z4miuCJ}7!R0Ef9V+F;UC1$~IXCa^z=7h#s-p3sjfpV*7E%qYm4I?(?R<)JRySVa3; zc)`@Y#TOWhUd5PS7WDt1Fs0?zsaN~=&dpf5XOvNY|7gCh;r3P3QBM$!F+j82ChhPdo5w z{rN{i5B!#Wiov{2!GwJhb_Vuo9OTZe-Qlv`FDGbgqDx!D610_pw%XtikPii*rwH)r z3qo%{FtBf1HtusQ5&LjX#J&HAxp#rDsyg%j_sKcAaS;?06eU-=YPBN@sZO(#pnzDh zg}$_7=Oq`eVyl!+wS#Q}0a0T$yEsh?ZMg}Ws4Z2frS=^`ywuW}Qk+j~$LZ_2d6C!cf9-fKVmSsf0B^{-@%{_VrQxH_!J8U15- zKFNen8H~P8iT=10ssK6gkpKUK!Ek8X-?{wQM|Ij~MeDv5eZLq(0 z>SqJ{1J6pEAHj|?FQI+(BS-h6n@v1hWF-rc7v;tjPs<;$;op4`&i=|n{-IAR_GTTQ z^5uvj=dFP@Rs3H=ySlK~4j;#~<5TVS!*^BmaSi{M(GEUz;h|4VJ3iHJHTpvO#poZ} z;TK*L<9!A2xa@a1p9}DfV&1uAqLX|#L#sgitQb)?`&XCr(&k@1yY2fUx!}|%Unw7w z=Pu&?c364n(qBghqhr4b4Bpc_CVk`dAA9cz?q790?D_-jAIv*O_V@!)Y*Q)wNNL`` z?9l3sijnHfnR(YbFW}q6m)OZ$*>eip)gu>m)LBLN`J5U~B>T{fd zg8hqy@AU1dx&KjJ$3BMaEr-?4e{Qm_&t@JhQ*lX-v;0|PU_SQM{Jz{-qTjayZ!WUS zJLK2vyu~))>-^=8*_@#V%r5p;j)Lx()!pa%GamlBwhn$%EKvZR zb$=(n#|?u9*wwB4Z_Bdwtl`=V%wd*`U-+!xcRBnh|J#q97ErUkA6r+=z4m1f9NwAR zd}8Nizc_L4lW(0+JRf6htMl!W1wRX|Sj~6wNIUC|eY)1D&H%hMA27kgPrn!P9{KLF z3($KKWACc<1zwT)q*V z{4t>ZW6B?x|CcR~&cM%xwv6>5*}h}64_V}G>~l=sM$dTlQ&&rui4NLJzioy)ex1P4 zb@?B4PdqFWP18BT)d#ODc~O{KEkHg(rHZOCuD0^Ui4oHpRM zQ8U#>8}&yuTW#ZA9(@j7vZ%@06h~JGPAhStayDh~Fgb#wh18cSjw*v63+}EwCB2hM z4Q)=P&f$L;|HJt|m;dvcE3mQC(W!3UCv5UQsnlTZ4QY;DWhY}xpe^S@D|U)5;6K6s z&NU&&*SW3K&b5KVO*^1_uC+VXa7%My!zY{DHhro&)-aUs=kR@4a|~PyZRmt;OKkWA z*KTg^*gl8*!})$L-_L8#2bUsnslh*}#LlKtx!^IVSz~MBIpKjlGd8IPJjj!|c!&n_ z)$OMzfrm}~iDF{x#GYsoI{4kxo(kwEA2-7Lq%=lD5cQStU6Bjo!89|G>MFpL)9~ANk26Hv9R!x7~FD zUY{iw%f@gnT|fB@)pvH`iv|6oGI(G9Q9ff^`fdDrc>iPgN@Zba0lf@8y?If={uHo- z^+U_j;HiG-O6pH+;6A&77(Z|ZmYy%?`QzYb_MzYI)-)qrL)$6TIiM!B&{BA=xwbzi3f>xc#ao2M8KwV{nuMOUu6TH#}PaC{# zWQMru4(yYWRoAyJ;(HvK-^%;&g^nv9yw=*hu@(FZ_|(3{Wt^!J`vEbgKA%Bf0pm4WPrz-@l<$22yQsWP z8+27&)@tfbArR0$<&^Eq3z4y{|fzC&u1sUJ3QZ7b6uL}O0l=VR`@78 zWbf^QkDBj)oU>bVmxpvM*Z#`H2X%I9?sD-{_Jy3Qyf(DLKEeDH@LmA!Kt8+dkQ-BG zw7k}GqNy6+ubjD+_`}e813qsy{_H!$9M&}vZ@79sUK4R1{L{%M^*0WG$@kM<%0}lR zOYc1keZughY>0k8*ZbS(lW>;N@6UcxzQUlE-$Ae4=DWKO>Tmg-hiLdV;vs>f5HQ?Ve|v~$$rBd$ujJ*;)i$0ze!i-<8#J_sl98vkD|-W zJH5D7@66<#1JyU4O@A)=#+mx_#D#x>{`CBxuAyh~fAU=YsXdP?=#O%k)9FhqI>@G9 z%Axf5LG~!)2gPTjzwm?Fw(}_+0gP4nPK(jK@`tDyY3$&P`wnEGy@G%Ip?2i79bMT@ zTi-t!15INMpTLK>86RQ}{>E^=pUe02jK3irPzdjpBUiHBJ^sWMjLRJ43_UH~;``y+y_M8HVDa(1=UO}Y zW%h>%wv$iSb_e%XTFKa5;IxWoc&?S_UiEpJKKJoi^m}_6PkZ+J*VuN$KLLCGPmHc4=0di`yxDODxmsYZ z<$TZ=nd4u}{P?59XuUqR{CmZ>-aK5`orjZN>A{=tuCDs)?zK>B)Xcy1-$41r{?5WW zJ-RoR@@q5vd}I1qSO|GfJ>>nSUZr>Zq(?M{Svkyu4S|l#8N0r{<}Fkkqcg^`TaHaW z*|Z<}3Qx_MP8+Zq+_4jhydM#ZKCLJq(YMpV(dF@w(~}BE<2k z6&f6%<09Y|cyuhoJ{OEE9+-~k(0$G{Ta&=dL%*smg+Tx_7$#_ylc zbN=ss`di#jf79z9kJno!-nrkbdApz|zZ|=j{2O*~-Fem?>9gYD_^R`vDc2J<GIJpwXB2G3ngC$cXEHbU-5WZ zeryNtPm$YGoTGdDy?eqVHr3jjs?FrxS@@%wEyH$T_os&(j4%hipehrXUlTe>w;8) z(vd##yNjP||H`OmqkeLa{L0u2#spug((G^bapz<7PxC9(&m?8DT^%^u?axYL&vXCW zR7t);^A&aQM1+99_8*->;$ zR}uLUber%UN?!l-d_Dy{`EI`l&hJafRTi4FA3BYKAK<{9PN$J3U)c%QcDOtrmVZTW86 zl8;Xm(Uv@WpSI{Uwf&mf^4+v8rY*WNY^SxI>$Y8~wtP2jnX}MbLzwvp&DSiPh8^Gx z*tPIoX7qc9FFo=76EB7KomgvSHoQTt$fDO~n6uVX_0WAiJla4m312J4XKOt%%X;2H z-aeBYzwq?t_@+b{<4^NmU?=m){V&Astj)yUEJr4dUsB|JT66~o6Pt0r92{(``g@!3 zPpY1q(QV~ajkR*DZgU@A^zWPNL%i$oX`kzs)m;Fr0(iIpc=^CH?ITvV@YB4B8PnzN zT+o%^m=BJM;TF=~%N*l1tAvuTUoD!;(hTd(&sU!ZvBRpY<)`m?f!u`yOM zqqF{SG%UaDB4hUsJ;OR;c%pWsm5gw2p~pvlZAp%Y-$LR8gBv_lg>LZIB1Xs9;6n}0 zpHoeWBeufB+dTgD=gIy=4S@Q%ljqDl8N3_DpXrSm+Mfc-T$A9nW>c<-Rrx|W=v*yyq5}- z!;A2bola#n$BTd``XcwzU1ji@c%T$o>USx!q|a-~Erh z-!|yF?S30%vEMf6!i|0#WTD?S=tReFgRESH45NRDM@BmvRAT~f)MZ+Y+ME1ge*3(- zT60~DaeRiG(|ZsY8cSW1Tol5O1<+IXO+C&q^cuXRT1DkiqY2Ade>~NcKa^St^i=6| z>-bH?N2RQps3T{m->dmunW1-&bMEBbG4zh=DWu>0HB#N+D_FOjNHrA=VvQQ_+X+oy z1P9kQ80}m#9KV;?I>9~FgO|>&tmEk8d4ls3a8f(%FY3N~yz|Kc z-u)!+_S?)Hpv{V&HshSq0ou%^jqI5Eq_tw`w_~cg(x=wJ`RkFqwMvcE?pmeBg{&3x z?$G+Q+!xXR=RBVBW$j{be0DP4M%EbX_}i)7WxUUo1K%%m>u360!v;Yg%JSiB z-Xq@TT_ZLB)~6lxJ5OUu4oYJxeXKJ~>O9)kaPLphRdjCUdj)rU{bVJV?u@}!+BeqTWz>mGTi(Wd^LNg!c zHu$8Le2UinRTHzF4d(x$u}ls$md(`e7`=%OFk^}QuW3tB_tAepKJou(KkoATfu1t` zKu49oIdDJ5{NL@zfUq;dQAPxqF-fU`C+BM)_uk7 zdT%Jxni`&t?|2h_m`#okKlGggnSE-z+ZaDn+dU>^@a;U1RnqY`zW@8!`xN>y97?uk zhkE?uB5LGKP5?MQ{Ue;8qWAfIKFRyj-%|m<>pihCWyw&bmHY@e%dgpqZBwkK+^llE z<nM4z0z9d*p}ICeg2Lp5Nx%fsNFZNOx@GS=s0<_-*Z8-?w6)3#bJueCwXq zOdOhjj&opX;i>(@Lk+%vvJL;_eh-FfE(L=*q8`4w_o-g~@%tt{eFtN?fc0W!jAJ2V zQO;VkxHpbf8b{Xqm~o_Sa2)?(5aY-hwBkL%Gvheq1I7^;W*iy22xFMVSY|V(?`-c| zqrcK0$7hMp;EB%d;gXI`&|^E}_%P$RfmoucH+Jb6N8tGK@(s@Y(EN+vS2K=j?@>Lc z8OLYA>n|L~!U4u{!a(B)E@zD6zAO73$58`~BYI$Df8*%!$!YYF?L4RT=Dzh+!CdXh^Ul<{ zkMlH2I$qhstt-)z)LgGv(mr#cW%DT_4VYtNe>y_K&+&>gI46Y zjroJN-&iHBuURGRJ696_rBxDp!zzg;Le5YAkZM9^uZa_5Yk!Of@kOM2x1a;`dq2O` z?m@=p0C`f`nN(U_5-UQFk4KLam-NO%?)pC8uYOH>e+e-Q@*{ZjcrQ9&S5F>+d&2!* z>RqHy68v6BTN4Ws??;iR)?eBsZ3pd=_Sfu^j^EfNx6oeea&2n5RST;;=Yk2Yzpr<^ z!8-)Ur&II|yCfY3`A8S9g)Uwn833=!AYQaJcr664AWU!+UXy&7l`hN+2Y@*{2$Qx3 zCi2_^6L@}KwYP|&-{)MIkpW;{6NE`y1M_npOyC9SxBm|*=01pb`uM|ceQ$IQ^#rQz zpnq1bbxjo7TiJXa*)b|&Uyr?a zb%cvMx!zrx|N7GttkL$?c<Ud^QcTo=!+4nDVU^oM%x4W8TY ztqI?%HQ~F*ob@@?*#zt>vkjLlklFn!C5@Iu#PZ>wS{f7bIJ{W{cTLX8d2UmRhCUPEtdz%jzUJ=~s z1Hk=q5H4*E-08ptrolH2?mXbikJus|g1*}#9l}{QTZqf_o}crcK03tjcN&~>A3yk0 z&s{tKem4j4qpiX3GhV+%&mdfIzRZUU{|j!`0C1-T;nLQ?o!1XsU}~H$@Zlm~f_v<( z{>NYSKRx45TLbr&G`PU@$A9$^#VJ91?CBGH)BIfY3G|cR5bsHE=<`-jZ^#zZqcJ-hp=gwu5iq^4p1b;j4bz(Z9IgPJMiSfOgRL zb-$fx3_bgW$N2uc-;ObsKRTc-f`;G0N9fH#cyvP-DvucSFMc=tO#kAo?lbFMj#qf| zNuO0cQ{VX{} zVkT#J>iW0|?tJ9SmJ45VCsBOh)&es#j4Bd18rW>P1vLCVZ637Cxer)&*6X zxr=L^wbaVRpi#&UmBbg+?Tydn*<0)OCguZ^w(@=S4!?d+b1a%`8Dd~*Z_Ml#%_C_2 zPBfg`(pF(7+nIytSW>sQv!-ru7yh2=7mq${CvPuf&(fiG@}W#S`DSS7(aR@U^Hi^> z+M3Q9Yj4LA@V}M#p@Okr&im)^86MIy2AfzlUF8cY{5aHt18&1t&41iMyPa_|DV&FF$+_QkWdXoFQz_J?Trxo3ku_-e1zQe7d zEhlPNLz-!S`|$NEA7hV`%%es4ZTEimfx|Z}f96EiJ1?EcYU(`k$~_gEI!=FXL&xd* zgE_RFN!#47XK%Wc{W^{ipOuEJ<1>D^e&hCC_ixnNiuC9GXGGiJ+zERlY3^j#66Q{p zG1uI+3Yye1Usdn=uIlTo(5W9?`cLe6bWiPun$rtj%Rct-;qmYO7sVlzco(-y}TSN`i4tSz| zY0as2-tkht+i!mliv3=nUqWWJZtMU#Z}lOeS|NkK%g4&ssb5iebRD{<4SCYs{tv+; zc0PRpc0Tf>XX@|`<%fNNdsQR3H``|2c&H%;kE>oRfp4-fC$y=~ou_K_=c!bqTQ}0` zZUe>^d{)8uM__Cv-d3(Wlle5Q{V?XMJZqyK}k(mczgGCMhmvGVaMEVGhDv#jLr(D4};((m)}W2uL>&UJGxFCe!GV79WS zhw?4co+95u-@JUwE zS@)_O3>z=^(CX6WUfpYI+|y{~_puAVdI$aMWB)56fqDV2o}%Ykz)$$Ud`-3KwV{j^ zR&m|N0(eh#>@CbQY`f5!H#x-o{Ic7oQLB9Q{qWVG({@8?<^x9`z?UxXF53{VfmT|} zrP>*I_+Or6{|0zt^9`2at@;~Xd6s{(9Ul2NYC$xH>)=z#b6q*QE{&$EhOp-ebHicD z!_vv8exGX@SrKlkCz$NXi|qE@w2`e-?SMDmv+Fi@zGv6%%=f6CK(zpx@6lZMZOr%R z^LFNY{Cks!TFKjp?=NpnO_5CY_nt3)i1+;AEbq}hy{Ba;wWeR~)rG3JZvN^$8;&f& z=CMcAKJLw0w(OK@B2+K*D(eZ>K=%i4;Gc2&H`?AD8ro0?{XVhm^QZoub_=W_M+@=Y zyH-*c!Fc>(0c%3;Ku=-?!M8x zJ_z4h*Pp<*Ue4!o^nl(Y8P@e3_|S{=>G{y|iKDL|5BS<`*W$a5$2VJoZJzAs7Dq6j zY1(7$j5nq z8FUp)fGf9Q_7bR3ofG$zYg|rki-~Rdy^yvhw#C1b&(WHq_KY?LPQD9g`~Yx18-zn! z0|y^2`aU@lU`cP*lBc|#TxzXy4cHC+uHv_W&v**YVFSP~3&N+Zfj@z@`Q+zf=(+fE z3tAInT*v=BYfmQQpA`Wnw2@4_N^BwcV~(Vnwo}s}I;O9SjnxBZJvi4|$x$KjC7-HX zNGyDS^B(j-NHvpb?|BJ5k^Y{4;yt3Ba2J&U@ zxp}*+fNvA1h4j{22}XLm;P2QEm|Fp-C^W32#zF6~`D}y6+u@@}#lK6S_jsGVd+;Zi zgKT4*x6Z`>Bab1T_Wi;idwt$H-J3I>PVE!%rf|?c13h(`9&Uo8*uWplAYSojbgwV& z>!-2qKF4b47?RTvr6w&~>$tFgiiwmz)*OR)V=?r8WeEBX{>uwxAHAH~i8f;O=nVeR z!JmQ$L+F;o3{!jGpz&S}-)av*#ci*Q3~!1ObNF^pHEmjFhz&WKct17ItSd{yEgjcO z;j_otKhgrnJZwrw@%oKsb>GWnZbh=_-I*s}OIz1~ou@3kWW7DS?{PQ!fkN<(LT#atgwS4U53m%UI>bV?t}WUVUOu7huJoDmVanNP z?{IxC!l#lSB73NqpvLp37U56ndTRsu2Ayva&YC1WB!4^?`o3__J+CE}sFpONVJ)&= zy|nt2bjqWnUD;9G{V1`{H>f{RJp3p=ko532Y64W_|22;uu5R-7ZTEE3)$ozl8DGU` zPk+<*O2;;RnEv`cWU#-=7piZ*4;k#Ue^31;R@?J}ayIm zTkrAPIDCU~AAswBF)jnqmb%)%Ic-^sxJT={U$`f84oCW@?JaU=iet7T3 zHM{OJxyK>rlRNS4bzhkff8BAGF$4pXWV!*yYK?F6jXO-Wl?MpZxU? zz_&uRXV6)CM{_-kv7hY6+3@1s<4O5&%h2PLRTl*te4bl0Y-Q$d$v)R3eiR&%_uL14eRNkZgzv_DZgM!DgdsBHA@AUnH z_lr5B1s#WunDSZ1QU2yY{=xIe;y|)i_*aaZ(a*hltk1al_gbGI<^%8c;o>p*ftfA7 z4v2P~4@G58GkOy!S^XFW8-^K-2w(-vr#HreI+RUx+Yz%*` z6Fh7)4>S^=Eywt5KYfgK>P6s&FXa2MvSICG+*&qWj~3~jnJtU(M;G({H<=%~e7Nfy zEFne`ZwnvAd7_tMCC^?odO3&2CCG>PSM7ZH@Ny2jmLMPgy+uRN6`W-mAYnqrZyJl~eHhyVUgwcHKsQ{d-;XcP8(BB4l0N$=+H&i@10% zd!~FZYo4aO$~zwhcjdWqSuZ&8)p5?NmX-V#vQV38t=LaY*~vQM{+?SMe6DP?c~0-R zU(e=x&vJG^f6sog_t|3;U%jU1*?;C)!MK~x7-Jy6zt4J(3U|$iU!VQLvDCchi5Xh{ zNPdjkfUc#BynNM*8?|p*FkiI-n4+!rN* zb8N$ZKVPnb<5W%5}Mqc5-xTjdX;mn28NPc4~{EJ?4$$!TewQWx|*LVS57Os;Hj zsH7{Kd24c9iD99VXi?btuU7J0)afYBQOq&~9_teW5m(E0C+$v+6s+XjI7 zoghrwnjDLAUgR>A^9sVO1g7#`zrfZh_A>bMo0?-6e|#amYt8`gS{-~BZOyyznasO< z{CTJFzn*sqR%IKt;rN&?AGmevs-M)}C1#Fu4$sf9l8$gA*Twve$#vB!*TwU{0G9;x z@@-3>cX_m$E}JACr&Wd1<;kzqW6nT$qQ0_HuRS2LkILy?gOh zycgMab#?DtW}n|h8BS-y%V{haAg6(!s+?ErTo<17$BguxMn7$SCH@TNH2P`7{w;lT z8vV3csy4x#Mn7$e)W*+g(5J!hp60YnPJ`c@ktvhY;My5E4WF0h!@Kg&KR|YF#cpX2 zg>>5X_{o1=expx(Bj3);Z=C5P&pk_i1Agn1-=N>hZ;01?|Lh;&wf^!Ot9tCR>vISB zrCaq|*ogP}?CI}cm*42qUq5E^{kMMOIr>fP;^sH#m+~7op9N>(`^&T7Tm4tWF&`G+ zdhiuLD!)?gm` z=*dH4pZrF;Jdodb-~;d-{5QhakKH}_?Srp>PkE+JXrFa1{;VI@%lGpAA-^G3lLq>)gUIxFMO!h;`w37D?CEwe*>NUxq&d|B<-PDwyQpYd} zA1G((;!_jw!zPo{xi8mFP8en-b#`wIcxS}G2i!gV%cvH9-Cy%+NRY;v(Wvwyj`7CHvwUG0Z) zKjRkM&rCJW%4aFB6^GxI&r(i|xUiXZ0T03+c1-^c>&yTt3efUCguaW(M++&$Gfmm=_sHS6w{7n0(J06JKAY z@5dM;E(-0lN-W-6O76_OtKQ{%y-PWcOb_-S)5iCBg}dL_tLJHqNA>L45ha$);QXI* z);7g~pTT@z2Jl10HvP$H`Fq|IpJOL&<^cRL#P^zVaHzXBssdXn`jsiBrG1F}##Vgj z&_27Qiggd6;m~s=W5d7hs|I!pzK~#QFAU{rRkPgdOS^jxv^JGDw{Na!?tE-|^TJ1R zn-_m)Q1jA92RGL)WQ=&Wav?eNdG4IvljI-#eMYp_yV6Gg)`XmvdTL}JzP`D&=>}lU zXpTRe!S^uVBh6blmn2F|XzVTLmen#hNUT3kcxbF+=y2Vu<{YMD_i%oU!O?!&f=5I4%=N&i66rI z+M$bbvFg`I-ecYiPWr6k{poEqPd^KvV&N^+%nY|0vRI?6nufLLOVwg8fkumYM-}j= z<8KDX?H?JpOX1&&%iM9h{xU0hL)zHR>t}4=M8;#;n>XfjcALxN?l>iBo9EeO@!z6| zt8Z%3_|NDUaQ^eK-u-rB*i*BHe!P{0Ud&sJbV|O5PJ#B+yCh>*@af5-zb8-|V!X}F~l-NdpT=ZnO-2KTU+TZC_W<_p068V`4W zp5SnrJWj-h1o8j%7I#mW7G@Gy@RYvoWk9HUd~cZ!dxRCd#wE8 zNlpdljKt7E7xvSpob`U?Kg)Brvu~r?3=g!?SXRPk@aomtS4h0tIRu*XU3r6Ri{Kgd zA}gS8`d(QZ^496OebyMA4!_eRUOYqpW5{wNSgyT7jOPSNo#1qEdNB?rmWVr-7rne+$2P zuJ&7@70jE>t34E2keTUaBYcbD~kC<0H+%DNXlJ^7m#R;7E z`}yzpz4u6Hg{6C!_1+7qe>_*qy{*9T^L%>W<-E_xBQ*ZWF7`ynCW$|#*FMhgnCvQY z=jx{{=O4d&qNxa8WKX_i7~ZUT{ah!EJjpJGktttyV7ISU{!Fqr6MoZo*+JRLH<){_ ze%m&mHGe|iBZGQQ^TpWDDc8}yh_>2?tqNHzuu7;6mVAQORO|)1P%x?{m$@*mC{t}{ ziR%w|=kC<^FSD}(e51KN)iY%<_7w&A8+B8isv#K-w%Io+&(yn#pLBnLWqncSB5k)g z?+5<`x!hHLccB9fcGZ0D3diP}_Ci(rD0b2RuHQaCzKRbY^p|}9C=veWr+r^W{ru9L zcbclQ*{hYl*U z9}U92${nBTsl9L&Gx%@?Q+Cqy2i$v8%D%?%@d4!je)v9k zo`r>OI(LI~tkr_tnmDXKpJ<;4{|_EqfBa_k1FsBO7+%FXnZQ{V!dE)#`yQNQ{ov>C zhcL_d(0$JftAxL$OI_N)_xL=>d!uCyW=>c+HqQUr!kjQ;{tkZ2roft=4aiP+5&NDj zVcfXq+L0;Fs7LW_Mlg4vu?D}^L*#lqs`7ZkG@BMEFJilP++B5Q8{fC{UF*}o@=u)ghi?;oZ1YAl z=Duw*dNb0pAAHpA`2b$>8N?5R0(|>MKlsf`!!HIsEM)gf?137E2aNCGjh$p6#JN)M zAwv~mjcKT*5??unJQOm9QD`gMSjw0^Nxa$zSH{Q3DKmi6|MqYay^hy={y%a1pNI9` zZ&>^HrdW%gVqXpIsT&5z2)buFe5-l=_yYKKKJU1dab;{1#DL0i)N}t)blTed%Vjgn ze(T2PqMzb})i0+sW1$d>-I8_-o#-l|Cf+eCKt>41SEmd;4_Gblt_sSw_oy z-(X)>`V~e0)$#850+*i1r}6uRhq0BnG7kb@L_KO2Ihk3Z(eYZHw=>5bQ76&X%`zU7p`-!W)h&e+BolM47m zGFVW`c|kdx@4^^JpNOV~@OwVvSk8FZ;IFYzpX1ycu099bFfMANd(z+134X26&D7Bp zgn1XROw5PR5RAJjcuxg9QKNV9FFuK1#Xt16xmU)umz)z#h3EovZpc({WNeLYVr&(A z#~Ir#I!7dWg|k14J#o=x32H60H}XIKZ}w=0@6z#_;NfKhKOb2r11~c!&}v_J!LKE& zdCbnXTpzW*SdHd3OVEacG0W)!nfVueewUA3qNPPGn(i8e)ZrBozCNW)T4=b@JH}Q zhT;b7li^c*v~AF}oU?=Q%NlEVriSmzBN)4hoJkLv8V65byL!>}6}2`ig|Bsl^=E6a zb2C}DmA5*yf<35N&lNdZkACj7LI=s${i>7k;w;RA_n*H1fmv2=Ll8f zeVEAiynwy?7t7TP^V9W#r@xmOpR-qgOE0I8BVT7qm&3b_-)Pn+lR3eO{7bL`tT_7Z`Le8s~B}XMwX?g>Gvu<2Yq&Ezis4xAM{oISDtd?-Klwx zA5Zp*-JV(4-wik@p@?4CfsNEa6Yy92tdB!_8o5EnVb8I9$4PH_}>wAO?jvj+`x__5`7@`BEjO;O;nUokbC zjNM%PxpUlo7sMlNv{60cy^NLe+BSa8lgz_QNBcDlZ8>g!N9|O@umc^gy(q*dJCUu& z&~cs6x*2@mBv)UKuhcPv`(}((pXlzL#~Qk0YuWezrSH1+qk4D83~KS{@i&T-KI`S3Nyx+4yOX6A$im|4&@uF%njBLH zQSbH7%V%HRQRC*7?*&(VSIy&n^i^{g>*=fLqZ-@}V01$NymPJNPm*s?|5e|3ALFTc z6OHFby#8x06~GicwG+G!#;g^!^9)hmqngY$JjxMGid3=X)*fL>Gj8d=RDq-uXlQVh|-6TBHR9$rQ>{>d*VU=`RV=P z48EZYEyvVy0#`MH!nxDnoZFJX&iFV_2;kglc+SOnkd@OgJGA!j;K-vV1}~lPYU{!Y4B`%Gw_N4CQ6x9S+XzQf)L zW}GBv$k=RT40|XYuKDiH8uBRYed5|YtzA=o$K7Y^&(t@55U(aYUVS5*wtQD!W*ztm z?|Q~r`o}*j;>2n8*I=LSwk7m~zDOs_4sM~JZ&LG&4VkCdAcpVeu19lq$6D8{@;+H2ON~r@UMIwGB9hyca)(7z5bqOR3(2oUtBaA$jf4(&AI$;o$~7@7Jw7 z$@9uREd(FMro^yG<-O|IKST9@75HQe6_?=8)MDpXgQsdK?a}z4%sCW4Y<$o|#s}@y z&xb6F{oaw2?Z`$@H>0x;l~-Qte82r<(+J)ZgmvzJ2i7CkI-7u{8ls8`W_{f5@?P7! zs~Y=8u4q>cw%6=gLObQhdcu(S8ne!cXzfiv7bwfqvb^SgwMuwa^D2 zv%D_P(OH(pFPQ^BZP!^7?%5~PI!?Oz4zHd8|9bbPH{Wh*;Xgk^c5f_sG<;iou=8nR zob+qA^mA=?Ki6*P=UN%pj9fB~vej4rnHVaY^T^ZcapkYiBA5H;VZHu(6u!%b9!pr` zl?@%2fOk%Sj!U8A66n|g9bJDbr^VP`@(_u^@;rj9xU22L+8N{(t>gFO2Pf#4 z;z$Ey)r_8a?X&-lezmrpY!aMGaM9S_lHNb)t~mQPz@9 z=KIMjmb9Te75B%bbL7{6v&)ZpP77z$Md^oN#=_+#A#%tj-@NZ?^3C|=_$sRVG5eLE zSEG#0_3W{(adQ3Eesaw9R*BY;_%-BDfv5OWxfrvSq@KE!QNSEyC)4MP&cMF{I0NEe z?EG^O{|a~uy=d?!o*M{%YHrVt{|M+6!2hf1_>X`l{owECiBOoaY>*gYGFe9|H&3TEDIAJbPgE!NG_9 zF1To2POvZXJp|{Mz|nW*e=9fp7e;n=Xw;pmAjtt^?+?ccB2*iBY&|X z`WU=7gX_ph9dVL%)5Xqy_9XS~R6G%KhN4@h&|dn(w?n(8y-oTotJ#0oWkGmTfoI|$ za94ij_K$O>J2^viaGQ9KIacyDkL0Dn++<+7`xh&(RL0m`5QOXZ!Rz0!;61MoijR2b zp@KW}odmeM=dVXvw(-tV-kCt(1@Zp#g8>?)<6V3v-X8(Rah?g%;`2T&JlX0R80MjZ zi@_^E3*mJkF#jtJ=4XJJhfY;IZF1Jbkl}z{(HWD{;fqhF>BfL=QEY$2v+2J5T1E^M zwC6E+BEi_puJ_KLaW)&{)QY`)sFj>SDZVH+TKS4?0UIr!iG0N_K}UTne11>$bpr7jyVD0 zEc>PRk7pdBVa*XucHa4y6HUsIWO=$yel-2RS~*(jq*m${^xfBW;s^2#=$9*<0qt5~ z{M3g(y+82S9U8#@XeOQs&c|JZ?6XI~ZaeJe$z=x|-}cw{ty6l#d!K*4{P6?cexK|u z_|Ad8`+e%1H~jhCnfUKpk;PKh5m6I3Pq~9vn9G&j*-D=L70yG*$+wgGU4`$Uc9qJh z;^!&{877z49u6OT`E>1um#urgv6dJ^x;vb&dh)D3F~$aHs@N#PI~8N->@elH{JD0Q zH{3N9ade5!CLeEQ9PA2bADl%FaT2wVOE|OZ;L>H&YWG+xEbys4X|33Y->!V)QF4vT zyK6Tr5B1`a10IiBgAE>jY`FqFWIxn*6I;R`UGPjhaw2=8__c_>X}y^8bH8s0=#VAg zyqy2dMi1>On3C^QBjX9y=J@*Wv+!MSzQeE2=v^z)Uw;RpLk+Q^eB)YTL!AvXeOb-4 z&F@$y-#Xn2HxyfugQ7Wl_ux|6W_GW8;CfAoJ%fAbl1iSjhcQo711$7Va8IQuZDg1KA=uAa+ST(< z|3&w??!8?mhF;J02EoCWBmWjMsvK&2jh$@Yz;)u-Oxlrdihjn<)i?nUNnm{WLzbveGwe~FqFlb7*izTT5_@v;BDqQTZRIzLhQvjl#| zLdLfe{#iSO@gc7iC!bpft*lJ^MB&AK`H2(poAoZ=U)DYOck_8?ei*s)+8RF*A9yv- zA$QoJDNdZRPn5F9F!CoqQE-%#k!RQ<%no7oKIDeR+@3=ceHMmVmRTR}KH% z?eR}Q=EG-!D?ds$_oDY)n`;Mh9fbEi@qMm2pYW2NTHP4qExjG7w5ZW_?Uu&C-v_mw zx&H#jFay3RsGbSJzs0bI}@*F^J(bFt<42fOt>T+DdFtF`FFI`~=ZbGK4kAYMiX z?y5l-s#d8C9cg|Wov3`H=DWfzlfHPug^OOA|1y3Pvh!(u0$aYV@58w|)U*Gz6(0nz zm7y~Vq%%w|Go$6FSNHF4ie~?oJkw9vJ%E3*3|sr>VR=3DBPY?QoP^K2J!d_@ryD%l zZOzd+!snT@4-AeKF5Y_g_j$MKV0?ehtF@%-_?^RJqG zR&PD5@T(>#Lv7meW#o4ByNX;)A+(8)uuP4P>cXO#s-+p{d_lEOBb0-p1}`Afs_~Ld z{h#zn-^CNE4Kg*g@K30iI+Td~!z-MB1O{;&xxEWbpZmZWT zu*nrQD&JH%%!QqgoEx0$`_`Pg{({x=2jJv+`JP*$#lL#v)|Z}Rd&aAgzFmpVva|8& z-s@ZMuJM@U(evC4oo8e@n={TmD*k<^$Z|$zSjiLV@J+s~SDrGUtLE(%z~dooWD&Yk zwX2)?y&7L#@*|lmqQ4RT-TB*a%cG}W=jH-YZ2A}(x&-|P5X!?VY^wiwOkdf#tyVvO6H9_>+Q)K9uHzc1|9Q=a+Tzo+)%hvNrA=6tu@F_PhPdw3#>9f;Li z_&4qxv}{h$4^rJtVgd8X=(U(^{~WcI&f?q@<3C>M{NjFUCABXY{)7Ao+t@d1gs^Y% zmDGuGzp^{UzF|RK<<_YA^G9{S55CvrB5SCtHD^Fy>a3^6Bf5lKB(yYk58o)?+#BUw z_f6_>fvvS9s+Tggd;PpCa(B_GR(x7~W^!Zd1933VXy3F?oXcF&Z2E^Cl8+p(;Cqdo z%%Hw4KEq00LChG%|H(t%Rx5WOJ;#YJVP4^!y2JACYKNW%KXeCUE;&$Nuh{`!*y1QQ zS^B;@Ty8#VvdepLkKKX&*SYRj@hrR))ECN|X*{OGPx@}+7U0X5Kpz=g^zI-&_3Rh> zIdEwOm({dg);fUS%6HSxyK7E~=67;k zH71YT=jMpud31iUn-e(T@iXmwJ@W3KdgVATAiutTo@nwHz4}x7QF<}Nn8m4iPGoD7Q=tr{@##{U9Oa?>pJYA66~1YGCxK_Fo5I z>Bc;8GR;i}?BNmAcRoJNrq;4I-=p(lP0UiOI?Hgg{E!g!FEQ{@UvK+@Y9Aw{ zRgl+xA9G?wj173i#`75tIpV{Z{Aiw!O~nT9vrDQiYic3=E6=j7DFok)Eb=e(Eo_&S z)GM!y&Og=2T;m98^0;ner()EWg$qtL*_5qjuaDeKHiU$l&muk|K% z1OWN8o8^sPDfCbuQsD9)eDC`_F~(JEQCwPi^C|c&s>e)kt9EK1Z2MthuD=pq?dN%) zKPtR&`EHOuu)o24RnWh90@?@1S7Un~Fyya&82`er*PL~U-qBmHnL;;f&3O#{5l5HF z9;G%j{u}t`d~ESR7#>J4)|wCL`aC>9E*l=$RbUTwVpDk*A42veMy^&lQo{qlP@Io1 zRzhvtRK>rlGlU;@OJ@v&htDJLfXwev{u3W!SNXodj_ME<$Hmd}i5lu3z%M~8o&cV) zZNN-y;%qML0k&(T@QjN8;635}9(t;0tj9Cf8>oY2{0;5Addn#9k9z(4(9eLA`jglM zJf4p?StSwr(~7*VgD-c&&*SiyT8*q^wp75=Cg$gvc#~bC=QNMCmbPo*ar!$lLM=yp zKDm4AYep~KkgwQ;zQB)WPTV@rQ4M?ne8C)4V|g^wIr#-_|6u3|U&?p(+tsmtBf)zr zptbK$=>5&)X}6M_vEIw>d4?Rip_}A3E|~{6E0lAMb?R&1h(cR(g(dHmf>#)yfEZo( z_aUp+soHOtdmn3GwpPs>^*pZ%m zKC(Xb-Co-lAI2Fx{n$S@-s`n5LH)6S7&~2mWB~KS>JL+od=~vwk6uWJf8^v}2481U z1lKQ0rv3Go)SEQcAPF80^&DQPtCn`=)f)LfiM5-$tGRH4>_dV8rP?2zVutvRmcz9j18-! z-D>=o@*MJi_%E?%Pd1ggzKg$J{ptiT^=2$#5~h`8W6VTQuOG-zK&VjnUITAbps&OD zCdK^w*S0gx1<>5$f84SUj%_~~poR9K_WR$n-s)oUbnkkr6gD<>D`T`0z9s&NEl2+@ z;aXk@pDU|551%ieyoy!#_m1qSc+H+qEHs`rwK>hI|59AEX&vh#SvOci?Nb7q8=b*F z;~r8?e*WK)i@t~YAvcFjU5eB6QDEGG%~%O9&8$0o;F`L_H^WPcr?ij4e$J_i%7)fv zH-{O*nthWT?W>@<&cp%iuy6)m0zAnh9a>1zVWceL&Y&E?(bP&?^Fw? zX_K7ol$agvi+7-exW=_Q88JjsFi~0W;_0e(o#Mo8*yye-wG%@~; z*ROJDBE9DK%m010enIO)<;SRL;~k$3^vj3g(^)hXoFH9&+5qEN`7f?@Dxl5G0F3nZ z&6lH_`Wc@-I&6e^d2CDdJAL&SypTK>8TR!TPVO20_?_hY%Ce!sVDgRR)AFH(!Pkqs z1otxFYJZEMUtSf+`}Maz^MmYbc&}Fj8Fu;aQ zC-UWk>$Q$oWSV^7tZTGhBl}n1eXq26bB5(;ZETA3e|Q!Dqnr0phfYZBM4z{kUtk?Z z^q|R6nLQJtieb(E*tt#tHlV_q~3SvpCOc9;3Y#22W4wvufD%;$C0NvJg4=MsX)U(wlMZ850e@htR6B0gYR^C~(B90q z^Xy|*#=)ihcT%G;iyG_+-S;t_&puA9S;_~F>VdTna65EUEv4wSXijEQ=aQQ5b2EE< zF7b)ZR>+1=J`SJgyL>OTTRi)ElLs@;%;tIew}t*}rH|X_V}g0#nxC=Oj=KD3!qAYu zsm)gUz74-a>p*C;tLD^4oJUztqxuJo>m$Nxhh;SwxD{uIi#}>H?**0*HwZJA`_Yxq zhZs-oRC8kL;K4=y+)Vm4CXE+}5226t^a{6pdV=-HaK_HG348pKJw18wNAL?WYVyWQ z;1~P=zaBCoewm7oB-?r|d=r6R4Buuss`pY|cZ}Cc!7sqA@L)vo zW#g-4Q|#`th}~VDV|P~!vAd(%Qz?q>fwuIG_gTqvyk~55a5}!hKIPM=Hzs@MmRDpO zAH8!qx}eN*I^hBEc0?xYlBq*g z&>EeCZaUxj_pQ`Aj>~YlK7Z`+mfJE~+_jk;cu zsokGoe10^*_*^3#=Xm48GkVtP4~IEse0CLl=_)6VKNTFGuY-g3`I6nc=)~JivzYsr zKXkl4+t{~>H(=lDbNkr0*z6GTM<^LvZ*?c?t?qcO)r}754qJ=Am$=JfkDkTfYsE%f z&u1HYd@-MrHSw5yj-V`Lst+5eSFXu%YHO|L_qThW3GcHg*IbW6|BvvKl(j1FiLTcOB1Lw9BDg&R4^mI?=&8?^m?V4TT%J zki{)_<=ATe>-qmr_Kp^hN;g*FgT-f*B@?sDJpMe-*~$KNVf35wW(%3yNuZBaE549( zuC<3j8}eLpPv$1WgY?77uNvEiK50iDci8uCYy;0I{fdE`?zJQ59i`L|+3Pp9gIgxH zMs%ryE;XzxlaC;~|A6u}_#BE8KS`TgGOR~@of>%ZZj_%E8^Q+xOQ_WlvuYj_`layi$Fx zu~j||cEPMfXoz-ge(Ler{IQbdux6vb`npgKc%Ua>*dG>&nv{n0l3u`_S znLO*RmyQuf->hdJ-xGg_80?yA*OF7RV~-&R_8_)9-M*+e|pSpJKvP5l01T0Fqo$?FnZ=WJs7pXi1WP=7^=A-zj~;qGVFv|gQ9o# z%-=rezH=q*TlrqBcWz={mD;-n;37N=0?nh) zXP;$!w?hlJ8(;I#qQ|Biy#_T_7&)C8T zZqB5f`%iIS`fuTk>rGvP`#rNqC%U%!JoX%Lbz>F#`Du<=YfN?a$U10|Z`SbSIw9aF z2FtH==coPaLpHqIbbxsu<;|2oi$1}>7fUg=$I729a%!*{W5MTs;rj$Ub`|44m2+kA zAs3Bs`@VS8bmB|@T$3zfAz~_GqB}3;nKx6)QIR(}$0-8VU|@{{RuX&6Ttsr}?01_A z-bwA*l1lB4{s29Pe~?ODjJ#da96Jg=9sK_QUxR)rrxt%3T>r?wr%$6r+0HkvVjol5 zR4_+ZtG(#Rnb!V?wIam+2C?xx-(V$wwf|((8ul!RHqeLh#5I3kcleR#jQkxd&v*OT z8a~}L2i*P>nW~^BNV)2%Pa{XqvSt^V^W|ub#)>_qUZ;Hp?ZqqP?2x6)o!GNH$MhEor{p6A{q?_M2#t@vx>k54xJ z5FDe~RyX<=9_o|F^Fn*6-EWe)0Umm3C5dzRYPHUrn2ZJh$kfhYnx*pj!tYqa8KEC7anZ zu;-b(+<8**LaYX#D9DhOO`e;h`a1Imag8Zs8!dA4RAFSn zU-y+FAFbSn@4MeBJ7H?jw2anIg1#;=_@5!!zBY3+HVng-=xLcc+`5$~DzpmX1;$>_7c z-cPArR1Pt(2j*Dn+r1$XEGy^N z5xjl}*H-c`ewu&}&YrPx@Y=3DVZix6e(mL=duz1L&@&BxI8%?_o5nvAO?`21K6+C= z^;nE;IpbL{x_WE@V`uSM#P~^v)-ZO;F-!NEab$c|qoVo|@q<6c{@5Pw=@T@QJ!8By zwomq4XM8lC8ehh3XYe|_3{LLF>KkI0l zi5yz_i^sl1n=Q0m$7g>2lCg2xWYAu|sM=b0eu ztubvYf!pe}m0ot?E;eoV^}th58~u^LvoGw{-TEfT z*q?B|$u}(fA^C=IVV`_MYHB2zx;8>=6G_HqN2nKyFh3DVwr(#hNi7JK2q%LV`Y4!V z`@@{mroMX%{y90aLj~V*b7bF|bh_yv^$~i8@g^>Eb*ShjdPH_1qZ{y%@nwBKq)=mi z`$%U;=ycQVWU_MX?i z4a5%QdhmI?b&>2-U%^?NiY@dU_4CXPU*y(Y?VI4lUIYGbxK|HsXc0q~2)5*>a(zYj zwakN7)z9cIAb+6m)a-ZLe9zCRG=4~Ywwrs25!=|NR)3UJ+CWS(oBTNKD;s8X`|*pB zNnqL&Tv$~rXPDSud}ClgJfr*kOyZn_E=((p<_75sFvvklvH9rQNu;aPoF z@je4f&x>x^);0Pb zo`C)2-NaMu0eOzuYh^2Vhc9zu8vnY+xPpCT7F|ajJZnv~KQn8ifsH+%gFT)p z^lf50HSfr3qRC2D&2;0PMbnARH!0U}zL&G|J`?U|X}$~dotjJf==Kuo_oUm~x!(S4 zhKZZ;M~LCHp8k9%OY!An_%QgxiXlH0h7M=J>sjy`i1)7u@IGU4te`T>iKEwlJCWMv zgWx}xv#MuLPj<|i&YCUG@_-jK2BQ61^6(b1QJ$51^u|JK-UFA}%@0l~Ykugua$@@n zoZGG-Pgiq+bNf`~>n?Ds=p^bx~-w2dt%9LW37T^V;2%* zbR8`3K8UVg&b9o4{`yM zmwjr5=OR3NDgTSPx1Rr~#)>hEGj<7R^_12YvCn^Wdv!^^K35`V1N2w(fj<4}Ogui? z`Cs=k1`k_FUtUAR(?(C2Si8X4qx-*v7m(wEI%*jnwv)HqOJ3&**6apipK8U0jT4-2 zTc?{UiSOP&Fw*6fu*)Ng#E8tj@llz36ITuTO6!Js1fRgF8Vv1$RWrhBjy07hW57G39r-2G?m%vDyNXcqeRExKbe+%o zbG&xvxa}&+lCcCi8RoCFj}rFoQ2DfC6XPzTKbO&;7;>RJop|P#@QhE#bbavkn|pPz z(Fr5Zb>i7ydwME-7<-~(Z~O-pdlNM?zS8<;8S{U0N}{ioCEMR9Pu`J-|GE--9F6Ra z|1q*R@pfcy>(Sh=wAE%L+r!YLeuT4ZvidN>S$-9t6*>I2=t~a2t>&)!@?@9tz-C{= z9QEN?dF5nNYcYjByk87_-qkM-@bD8CpXEM$JmZqH+=r(-&UPREUO(1)#OQBiWFR~) z%C{cr_)U4T6MA&YM}yYSOE>-!z4UeX?XygdX_uE{+7-+(eS7x+Ii@=c44fYV=WgKS4d6rk?i0QITYwKx@cKxz zHh295<#NFpJN(|%={+^b@<;wZ_TC0Os_I(&K9dRgAb|h@g9Z~|)F5cF#TKX7n@I+V zh>A5-w6qd3d}*xM%X=?QX-jmdXt94t@!quM-Uc0QI^kNpU`yxHUbIt1OIuLddV6Wh zOy)BQp90sOhM4!a_Bn^lge2J4wm$EB-#pKG&g7i4_g;JLwbxpEt+m(w)2zP#)w;?) zS*_D!`>+j9%xaQ-5%3#jKVqL zu;bHr!}r`h?X_oE?6x+l4iCJ%q!`FW{q-=*OCr!3Z zyIzy^1LY$2ac%ANVduB;KTzgui2ZQ8S%YJ+&8J;DaA+@CBnA-N9ICjW@m@UgWvm*`e%ol3O*ut*4Q3 zUkpA%@TGn5r7ij1SOa|8Q|#=HjxpMD6dP^9r&U|8{Pjqh82FyJK5e`=3SPe$oavKl!D zyoZk@-tBIF3R}hnZTJffTto}|+GwSX zF4{Oj8_l#agEnT;#%kK|(}qLZaP9n|(Z&na|2A5kVe%qvtR1KgyV-_2t&J_Tahf*X zp$+zyMha-7h&CLwv57W(w>lH(<1JP{;9Jq$FYVk)8}(w78Eh=Nt9!7;es&^4L9dYTHhMLzyEG$ zcoDeY1I_P+=6yGLV=KVDd!94r>c%&3eHvY2p!opq6Xk9_8=4KzW6o}6&bBaT{pb?& z&?Oe4ON7QayWL|9y!pl$c=L=h@a8x1wg_FK96o63642|(>c$yOZq|pB80XYv?Wt6$ zn{T)p3^{!Vayqpazncwv@(QiTHVi%0qxE#Fx|uVoZka>FjkY|)4cwR0);ohGo)j-<=@uZ3gfzIid8$9VWc^xV~!~dU!XGwh;b6)C$r=5n+&Hy)# zYS!kngYENYgWsFIvFXSc-zS~KF8BN#Iv(`B$JMj)Qt-EzGx&(t>1aT{Ef!v%-&MeQ zd0&24Y^mZi)!H5aJdCWv!%Rb6I z>H8e_P%m=od159kKiM$qyfRl)_F<{#C#-?13#ThPnpNk^cDur9%+DR1##*`aEWK{k z&y?s>U*7qI*}q}H5^J!7+X24)={RK%{c`%ZtNC`#1>c^m{v&(QhzVPr{_R@6U55?? zZH0YyiD~KyZ?N~sUgVg2g0mlNC#y$^9dF&vdZ`1rS~vr`4qZoVR1=w}gMELO^*#9y z0^_v>GH&bfoy8Vm*D-12{CJpc%%zNsgXHyjo%20~CZ5Z7ITMSjoUcqA*7n?uu0vPt zcvAUF;w3HLT83>*;1FNQV0B(^)k!{#4*0C9GtcGR`*YjprH*)Xf-?@?d@7fAnR6lwlr@U1pYlmU~=V@$GFB z>(qOxPu}Nnm*1~&O*G3&yYlWT?(#dt<(gPmHk*1~{EoD`;S08|XNcbu*z<@5jjWt< z&U)_Cdl!6)^3hGu#1?x`q}krnz7@K_Mo@<>RO%7Y((exNc)Z46yozXb!dG0)0@8e81t6)lx>N6 z#kO>T9=*Gdx6IDcu2=CG$dPtmUtpcRd0AOj(w6X=+h8|a#m z7ih(fmz)PKXUH6=E4zt(4DGXIzSNh+8ISl(SC8^J@H<-R>XEs7CBJ1JJ7zIp`~9Z6)o|2su3 z3&Jmg(OsH1Hbc|new$PLZIN0R&s{us@!X})7^OW`jom`__6_tt?yqwGz4^_{d}ruD znfv#QxnS%`O#iLsMKN%c0LKjAU~a_Cx$(5oAMA|4Qvy6QfQR`JXMUvTJGS<-J)v?w z_3dXvuVBv@KDc%G;OO6ohta)D84t!q>@pcPoRgE9?qN)RiXAIlitnCyhV-&w#>yB6 z>--KQ|FzK8@A$JGk9SpIYdX+vbfeq)(wNXQW_mAX05%Y5+S2QScG+*jqy zfF~C?ES>{>+-Bj^OiDkGPv+#3)5>4Ix&9qDdkeIWsb}5hvtBo64^cn3 ztjk6RZ5-KjDe-t==w0Gb1Q(IEvnm$JqJR2#vihOP%9i!+pSqi4;Ixt0`bUVZf0WpI z1v`Ef@wxeIa^BySP4Z0vacc@Uu9+&<>I7v~d|ufyfsXU+v{ zKAiH+QvSQw-;j9WPh|S_a}|zb-@n{nbfR|6P@%%GSR_ zi~ws5$$=j&$zhywbQgO)92mLJtemuQ*$2ZqHA&9B z6!xkP@>F)(3);Hug>6UeBifGHN49;4e*E&0Ynl?^EqnxjVicdMqrw^O%GP5ivi11s zY(4Q#ww~FONoUt-Y@5~*hce$rHj}{Hnj};G-wa`I{F~nvlcE5vKW3j}~ZJxwF z*7oRCO_Kk49RGj+(N8p;;s12o-@7Jm`;X0XZ014i{@amP?s3}pv1PqT{IZHwW54PG zHNK2P`!1W_XqPsv_20EkYu7MOpp9-jem~;KC(}ojA3p?b?W)q-_t}gyb2K@7ZDdmg z<0UjiJlYG|OCy`k_$Ot+c((~-6xxaIfJVjdieFc9IK-lpoUA`LYn(?T{Oi%bYkafI z$GpHd-HUJ9$N%D=j_jzCv$$Tgp35_dV_I6&_+~bF4CHwXdXJg@>@$wC3>apbFr4Li zfB2q1|L)4B{^h=7y^EIf?#hfhTg-P6$825Vb0>=)+Y7(3iy!iurl9m=yN$hev-OyK z1CH9eUFY9_jd5-76}>mB%ZGlfY6i6e^xvh_x1RcbOMQ|fNY2iiIO2zf7O4djs$n_DNOYUv57E z{F`j}u+ZZby*QellQ^T@@(emYfvrOEj_V{5$9&>iNeq7Sj#S9oa(Wayd!>uBlII=#|~}} zB742DE}I_r6k!J!Kcd*Sikg;{a(5ekMe#E(E7bzg6IFWjFmnp~dSt5`neIlW*LY)Z zVbcrU?);+Is2#LD$osL%Mm_lZ7v+7$;O`fpPedG5#O_t0zt)#Mhn^m-s8VZ>Ve-4Y ziauAdW@JAEG(HhM7cyR4fo2M=*@!+KDiDc7fmaq8e=0w|kF*=VRbG zw#ls@2iI}mXPk)uI{)+7@&28CdGv``>7FRI>$AXd#-U}vvc`nvM2ky5VZw8EiPm%$ zTfFrBA^N@smvz1M6b)>@;- zo+Fl1bhxF&MPqM$<&WLX*MY0Ez8OttD4RZ4@36{&qpv)R?pBbiKNfa7&(eo8e4qa9 zP3E`pf*L*E!npLKwKzCRf}b$sp~eNANY3{-vh|Cz*wZOv{%?%=3u^6t&^k*faLgR@ z@0w%&-M%rWADgRhzE=4;w$P7v#Yap%n{&9gFqevKdb{jtN~e+W*C-m^I6KoWW2vo71*$iIpHZ)Jf_=U z+O)!<1-?QKOJyru1-6g%hb?rO0h>JyHf*HfMZmTP z*!BXO57_XDCft3n-7tc^fZFnJf8qZ!#?+D}iN^*W)5(I@36q=r%oqKg?&hTyUJn_4 zS@IU^jI=Sm!+`x{^=#rvD07rDx&2{xn6P^;>%;3pV6Or8Ex_Ik>@$G97TEotK9AQ= zj5c7O*AMpHgTM|=9ISqJWK)Qk{+|KIM=jV7o3J0rgnbgQQ|5*0KN|4}Kc>uoAsaJj z!gr~G_t2$%u)BeM2Cz2(`zm0M-s+840=vBr_8$Oy82?|yF7w4W;}hIslQv>MpP{W0 z7M{l65!f*CllWQ28*Sn5R`jI z>T;WP`NsFvRZ3l@)KyMhZt5zbt`PpQg1)+5lDgbxUEjEqc~OhMsl3Sy5(l^)`cFS5T(f zqAP2Bh=EghAO`OISRVloe(=x=9%{h@F&2sFUC!`Y7Gf)2YitT`kmQr>-r1b(ITTvn;wXd2_VbsLNs2CA@hPb=j#a zm%56nYa?}e7C6J}`s%s{J6Lz9z>J?ORQKME%uAt}wKjuh)^Qh_@o}HUeFOU6Mq3a1 z2l{bwQ-tzABR{yvMC;ig9884-8|Nym4kH#4?4J{9}m)?70PV3c+kI4 zW_PK{gZ>R4BkS15&<8C4Nw>RL;nIj<(*m#JM@g*3huTn0oDDwIBlu85bG?Z*_)tT& zPLVHLz(Idrq-gq%>y;mR9c^~fW&&Bbk2ZTHXZLJ;Hnf|%!X4wBV#^&WJaQK#Lk)16jlEp@u7b0c+b zqD}{O&KH01oz8Feuk-Jy6JDk2eDO6#x zc@}j>Kk1Btn+!N-Q73eqQD)=^wd{LeLm`|pguP~>r&zmfwS8`!JxUQ zd3U7oDtt?dSK(h$bjKPk?6>zM;8h8HqzQOc0zawLvAmo$5OpX&Jas5PJiKc;d0V>u zmz!fE&$WP}O_R61OHS_d^vbi&^#ju@bU>`BA@6(6b~mqt4&ZGE>-H<&c5umoOx`Bz zAIco8e!-CUCn-~4((7Z;&T{g5JoYbDfybYfc;8V5oWv~%?L5YuO_VSexr{|IxGMp7 zGj8@Kiou=#4n?bl=%wl75%>_}QDW=)6|{KPe@%uS?mcR(ZDg!oqwUm8n{HzbBJ0o` z&Ylh1SA07>kGVXbtJc*KZW3jGA~r0H0-M1rA+8D?Wqy0`*@DO!LXS=!}A~WTAS#{3T%aX0(GY0?f(PoqP3u~s|5dN|5@xR>>?{SN2Rk(v0s zuLOLSTKN1>20pJ$!{@8uGm(Z*&gU9C5FfSpq43B@(rFI9dD|d3trnX5sj)Vx0}uPb zgQB^piPK~zPIt2=r_8}>&V_dnb6QK82h(upvb%^~uySh|xJ#1Pk@yGV(D7NZ{yyuU zazVUbN0&iA8f*1O@KuFp5HE!N>a71m*vzJ6;N}nJx8ahTu=n3Wtk^B^y9M5u<0kms zea^3g=kN^X?M&8w+Zl_}svgOs96rJK(Rr*L=D~LsdSl&uA6?~qwTSQGhl9PpS9mtO zEo`5I&)`0y&f4h`NhKApw{av)boAy`JepKLH9+0u= zwDBkQv7F~e(8jC2f%#E7ZT$93ck}Fv(8hKBXyfOUNut+#1^4g)Y17xqdD-XbA+L)V zDClA81@y3NPQVbYcTmQ|n5+<*xXT;cBQ)_jXO%@0gS`K(yce2iru+=b&y+ExykFYe6Vhiae;T8=Yy-k@gKmknzMuO!JlD=qs)0exSTSN zP=>uz=f~x8#>E<=%hGBUiqBH~OWrW`<+}6+y>EY< zm7^U{Uw#$A#vA1Ji znSQ#v`6~Jn$EUXUoEE@#^@7-N9>Z?;4EDMi9jBWoW4n8XvplP@ML&b>ZtuBqffwtv zCizz7SHRANeN2A_*;QeFZ_F35ca3dZ&RHf>{~7#k_?@w@&urR;Jt*A{^%3wPw!OX2 zik*l!nHRO5Z9nAtk!JYr=%e`V1UF;A&5uq0;wfLD^0x)1iob1ShQCex>T9uG)b}cz z+zVp+wgam?^K@6~8n8O(n~c2&eZ>LpiQ#&o`ny**sdC*mY`oCQOCN5MZ|YeC%e~I6 z>@o+JuwQ7eA3Ha`4e?*(BUjqfd~&ZndAfPJ>s)gsI;h}#>8QqUsd}(E_$Iivn0~o~ zb?iZs=SvpO*0)oiyAYob=j!F)^ErZF?q%Y9&iIC9%-Q3>u56*$uvDFfU+$pt%k9Dr zMl6j>_Hc?{j?Yvz^fm#A9sKSSx9IVvD9Hn>9VP zm2>y7TXy<2y~{Dod33v`AKRg9k%~4=zZ|*}YaifJemVB<{(^Hr&ho$V%kh6YEsi&7 zk$xPs&Psk+d1aO{}I6lu*#sL<-RJlPfC41g?)U)+c^pv zzfMl#7uG-IXo{=YfSk79OPPiN%D9X&o=a4jgX_OIs_9B>K;p+gC}ma*Q0DG_?VRju zCnRM&1C)8FUpw09^X>dV$}G+-gAe84q$wY6dX%_Hi4~a*4_*J6(M`kDUay=sFJ%N@ z%h%PY{vRRk&&Qs2*_UnM@hAAdKZP>=E;=L-? zRALNg=W8=xE!LhQ-tVb1p1h24nrO~}Fmo&Z4lvJ$cSwvHdm`_XSljvJUBMTs_6`zn zcIJQh2HSE3+j4t}*s=^Cgp$(_U90TNC*JOE{xoG3%rne+$-b6Xo;lm>&(XgB2z<4@ zhIUp%m-D#ex6&FNO{X5lp1($(Vf!VH!l&{@n*OZSZxysO>Bgd_pFkI_rRSO@W-{IYZ-rmSR}hmY zcaKXK{vBUT%#hrP2YD8JCQn-V&_^nM8V09tg1dK>j=_Ff#`aO-uLLgPwQv35bn|zR za}wXM{)W1;=WbY1R?XeReOcbJvgLW6vh`!D%WoQAUA}(&va-kV5#5BZCP-}0MSo*tXLcSw0x&u8R4xlY2wYTYC6i_G`sJS!m{Tb`9? zKHDeH9PFdyc~!QnJPWw1fnnlHOUkOaHkO=ewh_y>R`%>~Vt+90rN@?7F;m3isT?bU z!^glOvRuVZ5r?O8tGsN=FNwdqTIEyeljp>D^|V*eSK39M_e`X(PfK6uH!)H@R|$;3 zulfp3i0|r|B<}?my#Hr;pNB}%hw^eTiWa%dkQv}twQJ_g z7^vNk4A5@e(g)lVls?e$&&W)7S%y9kK44d8%;m7A&yzKM^fE)&ca*9%eU7Z@iPKIj z#vUHRw!Ba5)VHy($<2IPP2&o^V{oK7&h} zmaBaRmm2ns&=~%g^@Qw&(gF$m1c`<$y)};*4S6Rv5#AYVj}mk3F1n8Lx}?FWS>J~MzuPBIk9SeP<*qpuc+I7nPI!}8TZ~rQr{TX-A21= zpA6rKO|zSCWbbH_c9VQ3d-#&rOj7s`Q~d5F-dnEJvsKuqz@OdR*X16o`j!HJsV!N0 z3Oz8%nmdWSHgsaidqxiR#pe0mwAaD^om^d9M;F^%+L|&j{8|`O06_f&U^* z?2qKkO>Tf!Be$dI4{^~SkdsHzCt~pN#B|~|B}eKfkYPUfI{dx^U8EEFn?k=j>zKfr zD=%ZuT^@W>bh7XU=%=^_n-IR{8Mk|5Tc98RXPmFeI?4g<%!793Lp!z5&UR=AzevJ& zsk7S-?Z~*eFE#ezIG_(17mw`Afp+lIBo=1r8;BoF5R=z|TuH8S0l!O+AXnPgy7aK0 zF@X1XA_wAIUHX48FS?~3p&#l|^y4t;%u`~}8FtUaJF;)uq%-xL@1v&8%VE+P{*c6U z=#0HN%h7ogHGOn;1ReJm*d{J$%Xf)MtC#e(6{RipoHj!8oV(j>%V*fv68K9JrL=#Zq3l1gc zU4-#&XKX9-Y#Wm&zjP?MO?xAk`dQy>Udfu_an=mU9h$_T<69|ZO_R&M&M{3lN*-(K zwQw)>C3m>MqlstmEjG^76&iMDTOb9VQ{<16F-rnxD)bqrjA5lMSDh~=<2agmkXlHr ztcmAH zVk_ucxso#Wys723!mjVG&=x#t<6Leo*+ZAtv+=7f?4f&NIeYE$UHZ-Ut-L|#Korr9VKQZ==u0>?5Y`m3}VP7Id<2 zaItMfSL$AE!SlA^`=8_jC;M;ddGf1!>GSKK)fP0+=lk!wU-en)6#kWjk9EMKZn@lb zD37b$HoT!;>0z#&{{(LO(?V)m8FB9HwG|q`ubY~7FaNXG)-%!=Q~yX~OtIgmh;KUo z-p<}c8FTE;YRu(*E$`jtm~ToO^NmA{`GE_^{PDZ`#{9KU4Ls(71s5Ol&K;$Jqr2I+ zCu4mAp1orc|9=KvW9-Y7*hobSC*YI&89VE}$Jk%o?a%LfU&;IF=Gd-C8(Ys1W4q_V zv3>mZzOjAnwt>esaO;I*D`P2Ry4aT2fSm!oy1Z=?;~DnhcPPMLScJdOaf>&$N&HF+ zoW2)~g!MoArm$f+#quz1>qWr_KK*NBYCgLXyE0Gr}pB5%f0 zZ%p_Xzm4;*hiCAwX7A}{sWS%uN{0y=RPwn~Kz9#mVIkc(v5=}=&^1t=oCC|4b|7nl> zb$SfA;@~JDas(VDxrC>5aCH(p-o)Jn2U+iS z%tHT}0l#VB4m>Lh^ERehH9h}oSD@gdRqUU+G|)BkvOqqtbR0(i+Yc_cRt4Kz9tn2% ztAeq4Zg2c8a2%TBj3V2T;HvW>>^w#2MGo|$3ek%Sy)oiD;?eQ!IA1^6BRs6JT#iv^9+=R}Snnz!Dk$=HE<=kN3UCEr0@1l%z-NRGPb+o$lsn@y! z*MO65w~_at+b83q1*U?V&OMBidJfF$x!jHY_T8aEgZ~iU&`DfdC$`D^8K=(a#yAPj z@0x*a7&=^xtb7E%Ap3)7)i;v=oD}WK6F!jE=8>VtbbLUK#ehc5QrwUzdxZ)qNjYh6a@*V0b?<(`g z)O}u9Pp-koABJ~;Uo{@o)yZ6rG0(_Xs7qV70^8#cU-L(?!%F>9x70aIV3Y6Dp2=Ju ziEb?AlpJFU%{VUhgDwoT8#mxA#fU52rjo^ zw^n<8^4J$=YkO-AxmOkf7r2apQ{lOZEx-#-$0?kq+3@OaAg<|)JKsI_-f&v?@O|gB zhbzuC%6*vDGYOqs)(zK!*Owk%a;_rVwlS|TccYfC^<15&HSJ|D{z`nSvYs$#BW=%| zxeg7()8u@YZ$`=U30|e2%NUbayc5rjKrgpwLvSecq1K`U;9b!TxVG^0i!;5ud$q!@ z`#8(!O78>bu%R4WjtwQ3-!EZ@i{vdY+sis4i+yUxYB+nq`?+(E@+_OXkNcJ61kXOW ztgI3H-1?K`IcB`WHOBmW?N7Z6K1`pEyLpxd^RS0MbnfRekNMVu`K3>kFyVec!Tk>B%Uf^{BmdcQ=p_uz$vkp_ z=Tezuvrmg!_lp?_BE1Y5P#x?`h|M4{MvEej9$*E7kAe zd_SV?oNjM>LhZNfvnNK7*(!G+^U0W7qnm8ZE7?aHWsXa} z0GUt1BRu%^WFE9I51wZ|U(f!xo1qz3FEmra_Z6D%!G29X(jNAz^oUJ%a-MB}7WGkE?r~qv)EKwr}eaBTg+l_wAz()=@Yo`w-ybpi&0bWVkq3fu>HW_iDf4#VHf4;IOKLbxY!WiDiw?4i* z!gm42b3NZ2;hPTd@D;v!Y>KDs)hWx$USo_OL8goEReN~SIkBhKVUwMR&EBQmRIZI* zUMA1wz4-kj_)Y}ojD5o{u~%3^l)ikE^lOCbe=&E z)4)jtz7sZ|#gONU9-!rPdiW^xAo+45(6YIPP_!~#o=>Mfp2y&m@p;e+JTeI{m2sU3 zt^D8wv;xgB_Y|!xhgK%xHxpXHm$P}2_R$MyB?Ya?KAT!-MSf3#R`Swl#Rq+OpbxQg zif(chwDT&oBkKM`Jt z1AhYelhD1?rTWI4$mkpIgck3Ijzm@pzvzJ0WFOtriY~Ld^7;QK57+Z9t7|TJo&t^f z`Cn-EKFa+Rcv|?K2VXCwuYELS>|2+9i49xUVPW7+?~_4S*=>8lkMTRV?MLdj#HVMs z1<(J^ZTo@xEwq>2wu|2vqp{0|ps|KD8nfuj;5U8z7G6t?5qz!^ACOz?;k=Tb_&lx0 zl70J_SFhpscpd-8tN1>`_;+kob!C&eLj3-?cx?{zI)Q&Dm$FIxNvTR#&*OP3%R1ar&wXVbsvhpD zp8Lu)+ApP^7(O7$S3%51PYyi%D^D#i--rM0Ioe9XyC46T<>kZj$uHixygY$FDF;~A z(}wVnh{;22^dpNaosU0?|I9wBzO0^pSHn+`7d>|TYjwy9@okB&vt(6G&%I;LG-uI% zyZEMP&w740&&59_{zvr=ADH#-lV+JH@1;H&MB|J@7P# z-5GbdosnIxV9dk(+s%Ia)m7f`LgpVdf8EA&%}MxBiuo%!ks@m;zmD=gEf_1XdE+%2 z^EAuZF|7vM-Oc2p$qsbw8WxZowjIbee0{{avyXov^)8}~Jua{0Y)Sa5oL7J8T(b`W zBRoocG_pn#z9und^8cml(YxUZa@M4QtMfS_@NED`32@BNyfOGQz7y8+Wo@?_xZ*u+ zme6KePK6-zM&`?34nMA)sjvR^@Duss(D-@7_vhm$`#s=C{J}s z?I642o-C*2ZisKancNN8fq3b#KotHh`po&~TWq|~GM+<2(IVcno=4vbuA8WRp7CD* zZx4n|o4m+!8T)P6@<+i(--;*?Le>E3p=6{%!_8;IP!lfAL=MXD5Ey!k-_Rreq^HtOr+L>4&Sw6|S<|%8`wE zL*S}|anRV;wgh}lTsirV{c)57ZZ78<%Y{y|S@!$e;A@iaZQ+=q@g_1s=H{{?a3?%A z9e?>M*U5Xq-=9x^c6=lXe=fr>B=qM2ZyzKLe(%WX=2y6W#C0(mEFJ=nFQn-P>H6pE ze?@vEr@(o9hWgW^D|7CC7aClzaGkB@@B5+2CE)5|u2o$9X>#Z2q48+ZqiZZM~i6mK5*5@wT7!def~$$(0H`y^R+{R&-KCR^OVn~;y3g0 zbG%zLrq5=#hIPSd)3nK@K-yhUOwm0;$hk;7dpGdU0*%U_4Upg{CuptI=8rjoef&BvzhxU?ps~K zu31&y&edA5t4s3|r{(OZrCw}}9nlTdEC>IGsBb&< z?V!G0wjgKHs0p9O}$9`U9;V z2OlRIprcjL(Q4?Z3px_JuDzPLL5WSlpG_P}gm{&7{0z4KPRwA=u#P>2M@!yZGXyRNmp4ZK;P-8>)BuQ}4}k79dVN6eb!n|=ga+N72BHxPgH_mN!VJV?XVZOibDiCt1`-q%8Rv$L*|Sx7l+K|M%(Ux4AmFE`~?f5O_?tN4|IZ`+tW$_v_#$z_pF5Kd*}x z4vja9*PS{z_}mpu=4lg;PsPSH*;N#23v-};|D5#!RH0?*EN9r zRk(it^7j}qHQC^+n|ptn{JZx^leZ3m%R%L@-B|Bkt!(6d`PF-SZRpxrfnNMpcQ&wg zz~>b~r*3aS5B0Nlo5$J>JvJYmiud{r+>{+g@DvTmEnx()wo zC;D;cJl1P#(Y?D}-p=`~*Zi#4d^V@GF6)>!oAugFtk-O;*Rlc~=+_;<7RIL*aiC}8 zQ;XKRh+7u@oO4cyZIRs5O1HN6t=U9}&NA`zQktx=)?@$MtZxmJOm#9H_3}7 z;Ob$nRb0>{`)$;EjaasVrk%rw#-l}(Fa2`x`8=4s5dQc#(P!$?^cm!C_ydfQ$N|Tf zbB*P?7=1PjfzuC^KEdY&`b^yb`i#Q$``2fJ#M&I>+Q-$OK8L?Y`W!z5EE(fE}zm9)}_`yg1_xM-F41xE*seh&5OZZp5yi@!uSJ(aj{*}M1f2F*w5Ssp5 z`&ZWfd~p8wf%;drrO6xk;s+^jenMQ%Z@CU~T}<9o41v!NRNjEk3*^nV0pyLs_4}7M z{{XI@K3a4O@({Cr0Sk@)u`37r%M|Fkz?phSgu<)_;Mm#@g@kI(~@s{bk$a+^+@G znx5ti;}AKl!bdcxL*>PjwzFiv?+ew>ezfUs;0a~3|K^CxDSdfK@-xPYR8G!x7^rWv z_PDJnLhiuy=K|AeV3Pe;>Gq<4f~POP)*^6OS2|ml`x4GAd8V@Q&G&E%$uu!Riw_Jn98^R#J36-GcT9GatE+TUSNSm{w)0D z7iHq7ca`Q{wqw(qoG-e_a4xkarW!Rxy$(1?QyH|U$WP&w;$lVW5!r= zl(I+pDfK>NzTe0D!}5Ne%3)e>z29%XmptET?>}w5KV-gt%c|eVfqJL)p1k(bj>>DF zR{j?2ebjuP%&Z@qq5&WIV^w*{qn%d%)ojA2}@q{>^pJfqD-eq`x1|`~GzJ#?MYS zuTL9$Yy3}za#TFY>(huQ3FT$PleAaZ^w@OrFFMG*SZ>p!R}#n?N&e(r%sb*= zdPnekJHMw%EDU*u9aZ{Icvi2@2RF|%g)W|!yod{KXxhR4JNO7Zf_R2q@PO$q{jQd? z%~j-34ewwtCb_^h01@wD6aVwlT^qj_AB>&`N@Qha{PH#$(k1|Y|2(&D`s*Apo^G^@AXgYh+Pil=f zHhp^O+s(2MhrI{-TK1Dl9$OFlNhObp>=C?$cpv%guv3m$7W$-OVXo&LbN*oUi^iP4 z;ScOXB&H^^SJVI1&fep_E5Bm?Je7Ejl?bPS+5BK(#;YTPp#-W>dA{mEw*b6D+U{4!|UU28n zJwFalFb<7d&NffCYfnkN>t3Ti_9?m};BdF5Pc-*(p7y-m{4_aHWiRK;k|S0d(_{nx zGEUaM-^gxboWjH>$nQyvla0Nc?BT#(#+lZPNtiLYVK5xh|4+Wz+sv43u3kU7$-$Vs z^84;)_$Tw{vOpFzT-|tC6FgH_F;;Ie#-1GVZ%CiAnx3&4H2gH@d-nED03Vkd_~?Sx z!ACTykt3A6uBSEK2fcWp%jgNpI{NT&6Zp6hd?*<5#wZw`6&OM?kH(wx=+Y+Q>h$nF z+TTyzcIHtf|C77<5fg@&@$<;|sT@D}_}iJ|QRevN%%3{W3smpSd2GavA?r7uZGM^a zfp6Tz93$>a(b7qSmVCfq(R7NrIuK0@kBJQO&N`=8&6De#N;W>2R_CEX%2kU@UwLVh zpZve|Y2WV~Z%CyITBP^tC65pLn+@^Stgm$j{-*b?H@<|9X}w4}6;98B+hX z)PE9rFglAo-+uB3WR32Ua|q5jw5G9HntqCRmVR)t`lk+1|G2+k{a^k1g=dcUy!PJU zKNZR{;w8&PzGY{~x86<6g)Pv&ADVCB_g;*uF7s4sPIi)P9~PEtPsoc{Z(2HXN$w98%GHP1&IGv5Rb&X7HR4F&6S$ z$%g5DvLOfAVDZ@R{S?|jZpsyveBfZfyApX=yP1!M6p`Cp`7i^p~zVNTELAJ?!SHq>pY(Mr9i^kG|^%Gx_W1eg|)>l`$G_@K+ag z@W0x}yNEI7jP!_%wX7r5I2V9}!a%$5G!OF+9JDVs$FxDy9~C(aUu|DO9nmau+lc(i zYCBQzKh24zOul-%v|U5n>++4ZU%Ixbo&S{&d_HYDXd7CK5OXGNhg)3GoJ;=|^(g*{ zUt8p*#XrA}p7Juer;mOO{z?AmbpAPmwj}pK1l&lT!Wi}aX_M>x87@M@f7;Ub|3LT? z8khAHGE9|q+1dBW875X)>;HkuT6`{@|Bn0%z+}}6|2$dExd$Qm=N}LE^;KXo^b~^z z?m<2c2wyI^fu9PG8w-yJ!viDmz#jPKd*x?ypl{Vy8o!L}dvE;az3}5dSo(Qd{jdKS z_1~xRrIhx?+QTP=j@k>L*}9LTv&lM1_J}ureM86D<|Uj>pmbD+#+-BMHqOGWR{HrD zIQKrg?N0XMzdW3LPR!{_^1wHuJ4cuolMB&(h|60{9u^xi#-X{qOGo^lH`i&|4U&7r zk;fU=+iah&LvLF=>ijpsrO$uu&DgN#-KF^0z&lxEAL}o^_Ga{J-+Ozp zW^a%>CXI4AD}UY(jujWyAvskPeUM{Na-$CgbN_EI_&<$aS3(08y?!N4{>}ww?-uph z3&QYnSr;{G+V_n)VaRoPChvq6USeIWa@50fgs*u$Dr##(q^vfy0v zOVlI#7+k#9pbZ)OYemneoRs~O{ao`OfydHSWNv*T4c6cAPUG+V{OV`lu;7JGUK|R> z>jr`GKY_6k*erN{13c;dPKT=`4X)pra0zVk_x~6^wbmr-A>eDd2z;mPd4|3x z=gZiKXXty#O(V|>`Cd5xN{?*G;{4Yv@?H${#>j~q_YZfT@#kcmS@9KOza&R^FSdX) z?#%NnzHGglg-xIa+sBq_>Z_){o2ZX7(EPVJS8z5tYs(jw%&6&^NRE^UIb9}_cV#L3 zQ}()ygf9+OHjlF(tg@P%EkId0FD$XJoAyle5z^ylM3!7kh8=0;d<{2Tqa)`XqYd$(oz_e=g5Qa32{sS<9K^=)&ip z?;9RCeVBbf@_Yr)7rOLOtZh&3b_L77WB6K5?s5f3J=5oFISGEn{te&z*r^8O=@^$@ zoFhJi;;9lJpBgK9kKD`d)j~6UcA5f+?KDFXKAPA7V~^KZQZ|G<&Ty+ zN0+${Et&{TZE^Rsx4L>Nv$W5jf=6AKwfS44cxTV5FU#IsUp8r!yW9o6ke_TpuRG6q z7TWGl!}CmBoty<8pz*MOjx%x?*|3NGOMA)x>LdSa5?s4$oXHdHdtz@>2YZ`3+1o_y zrGAvXO<{Xg&oRf%&f~y#f=lsc`*0;2kHVXd(zfJ)Oirr`rlwQgQ|;|+kvy9>Im7Fa zH}M@XWYZSK0j|*xqs5Gk>8N~ zg4j5#vmNUE>bbw|ZXO8VCx}NAei8%UVqc7<;W`ek6W}@tu2bN;16+54>n?D86kH!O zaeW+IpJ1F$s@y+CZKsqCQ1TSZ8Q(eN;4)%aQ{=YnASY`lISsqWZFrO%h{xDNcHBOS zaT=X5PS(DPw}_!R&K2h>M~_glwU{{Ge))sM$AnJV(@st((FdHbm~yR{*xG)%gv8f` zzQMPy9uBU_@IP?qkmA?*AAw){iU#D@oMDX)pB-#3$__>ydEQu2mLB(wf>#%2@aj?% zx4nDe)#MCMKO<_a^{xvWwrOJ&p56^lSN30@aqlff4*2e1OeJ48`Gt$fFYMrq)uL*B zHvBZa3b_$QZX}T#U0K1{;bGqRTAQAT7Lfaxvk2vE1b>0beVnK$33N^$A4rgwHvwNu z=4#(RUdevOn!CvzoUd{_cZc%fOIqOge)bl+fk!>34)xr{{>HKFK^zx2?yl00!!u={ zr@1Dcj(5=>cnmiq*Jg0G=ScBrIcsu}JD6JR4t9j* zcsqAj>0P1QoZ-DyJ(8FDn8gQtMaG&Pnv{HqKf&f&ZJrg<_+K(dE`(udIY^=fF+bJKJ0dM5|D?2({4BUKX%=3m zc+P3w!|&#>P7-}>wyg2GY(2;K-b;DTmVk#oSSdIjX?&jx4IeUirpi;V>Vb~@=66x^ zyHdVO%2~D-&g~B)S495(#raDv@~8lLBxhz7YvPX#wohm8iHDqbtcmd*IYmxw3Mn~t zK0jc%m~op1O-^S&h==_k<&2xX%6X4zFFIu}R`TiU8d*<_%aBir&^Tw9{Opn1VdU%^ z7L4v1?v3q2Hu=Y8?72_RdC={Uv+co|XPlAK-Jfrq1v%N9+f8244Q}&VnBYR6V7rHlj-y^l0DY1|qUMcx1(GAZGi zgS;tkTP`s6!nf_fmz*qo`o>9*3C}$H%+m1nCM6VhU##Z?D4*0gfxKQ}^JYc>R z*mJn&22#k9l$+{l69~I@$zi}>+q6y*y!sz3XZgA2IPVCjeSix*>9R8Sa z->h&lNpLcYaa#*c)`1gyzBg6_PTXUVzj8hoII$b&O&Q~(nd1Wu&l?0M6{G0;Xagr( zE*v-E*NQIPX>&&SwR%o_>N)qQNxu$r&K!b%(Q8wSP5Rw81pRKGz<#>R4f=h@q+ix7 zkwf6(EpTxd`q%?~oCg2?8t0LP;GqIM)PjeF;9(cEF#|mKpU4@|!^2f;%t zdc_X(3Zad4LK}C0mrwT5#ukeOt^QF%CRj zV&G-Z0QBMEJTT!~{xn)H2i78BbpUGxuucQkMZk&-Pect`eo$bY53GJ*Z3Wg^U|j^P zQD~Vn(+u7?2rVxj1lHCuj7_ltD|Vbz#ixM%uD&tcV~ydRd{^5yhOB?y@BB{BLz6C> z(|LyQ3*i;QCxl02oGk>;lQTjLJBPtDtg{eV-w^X*oP~Jvue+OfaUQ~8?FoO9Hibw1 zx$O;B=lkkIuBH!^%QX?wl}GY=j5SmJ#iqL_@-=lW|e@Rtb291@n9+{0D3mI_;+$*_Tg+Q9I{u`V~*X)p42?X&Ag-DOVBH|;s*D|4Q8jAtEwc}72$ znf*u*1DW9bQY-h5^1D81#PhuHtM2Af+DKS&F!TOi*-{W-k(66_U>$~ja!M3yh&-KQltRWpm=#X+Io5&K;{khYg zoRuWHh|&wu!;aEsB4pE(8|}f=CVQ~Mk?ZX|VAH#NBjIr)4Lul{WSnhOWaz=bCTANZ zkx5D5lX{iDOugzmc%gCjsho+#cj!1t(Q)XT`c9Px9&}vwU9{Nv4m~5e!O(@&cj!V= zU(zuSo$Qi85?PWIo~g=7zoeYh(@#G~Hx~W8Kixe@d}TV_sWzZHqi+)pSR9iKSfIb; z^lbgCZxZ&{@fqXZVb+tJ;ntIj_$E5J=?wIdZp-?E_03Ga*_5p(9iO5PcQFQ68100v zFxr8KCjHC<)d%U5tTDJdt~9>$Txon)1Fo4bh7HivsG8mhkiI))v-8Z{(N0> z;d@G*k=4wfF3uA>krRwXM|k7wm_POj&g2H>PcieSg!waLq_=Ae^T$8I8Q#kLiB52y z~}j?pQA53m@^*c%nIhrp&V~)4|66u0$C~Nx(Q90Ybj_}%^7G`%^B7aN1^$|R_4qs z=1e7X#+T#mY-P^4DUY6R(A5ZI9M#;=)SOdufi*?aoaY&H!EVk4ciLQ7J;2;Tma94A zH_oqBb89i*sq(GfxvE zc7D!4L#mEwnb9}+x~jvPGf8OOnlt`OGRAs>H6MCu$C@+HMEaZ=ZGB@m=ZybO`cQk} zoI$@(b4K++`aH;-nKAgB2_Hsg>_KMiMP~Sr8A*{DE1ZR53)%o}6hj*&&_<~}7@L*j zjc>6bCkoMrWgSkN9>d<2FxP1b?3oGHX)DA}4}UKfpIx6lb#ryh&^NAup8MnY<}@4| zK67JU%NZpaXGQg`tJ2mu4d_^`aZ(jx0}@@4HDfFGvK`pV3aa!JdVdO?U#+j-GVGXY zea+b*_g@ZQn8tf+y=JW?rB2q8slCYDLD!OEyUc5Qa{>D1YVg#R6>L9&?J|n(avgKX z4vy4%9~`OmzKoBoFEz%|I1e`E1HNv<9$~C`<+;Fbt|zfWsP*Leb)&hKkaZ(#iSR1m z!#3HTME^TPKFYP|1mqM}&RBRcxDJ8qec*ZvxNZQ~9_+-d6BWN= zoj9P3UuDYpdGM>PS;16GR0*AfC+PS4sbE@Q>S(sr~rWI{1ghr?4la^PsoQ^2z;ad_X3J}B6^_m}PEgY&5`vOXauLf7Q{KF-9t=I*zfBL!bpXJVtpxSE=VtBCj{@H=V5b*A&6u%GqDJR56dTQKS; zCSZrg`gfQ!fuBosvD99C9U=1lY_tWtHrawl9d_?Ac*t>Ijx+ogYlp+wdyg3Y$4mc{ zwTYj(JFklR(C7X5l6Nq7eK%1bxRUiGyr(0C-3J?1*QP4!b5kESaNiv2L!Up4K7Yj3 z^X9qCcqSu%MOwVwLe?zPf!(7a<2Cq%=8eq)_NdJ%`cQJChQ6zz@7g3c5&CYHGdvyG zJn)qj!dF;V&Jw;-<@6k7{^9c(?0vVq$JZK04~uN1{3gmf;7jwRZ`ICPc`v@bLEd-q zo_%)8_bUE{F#dwaj`TLqn#g_!@f9v4CZw^xTzjdm?2G#8=Ddk~v%BS6YMjjYw!wVB ziBkGHi@sLUSD)tXte~%M&JxDoA-47xe>}u`v6L~H<)SQkRDI-dsvsV~T_t$xc_uBM z?3Ay_u)og{|M2LH^U*Q|k<)i$p@R;dQg&b#vi$)%sP$xrB46tC+evxdp6nI54vvxEN?wNy zc`fTm1+!gjgV;pv*pa3QZtX zfv?2vbDaN@fQ~Aei}RqdtqtoL1+1fpKAKXc*`7J{`vI5UZ1p&zd3dnUcDXh;9PC?1NtR zqc3mG^~SrmFH2Nr}KUSNmic)BBLLen= zbwO)~p&R0=+V?&ZjE1n2Z%m7=f?tf4`LUn=Zl%91^w-V&!2S{sVK2c>V(7DC+lT)9 z&yT~vIney5CFZhzjJxQkg)ekIg$Ix6Bal9<0blAl z?W^b9(L2<4QIj960gejlDKYUivkzag-s;-50KI8E_e%pE)Y!wtMF z1~091`TrL5EYmjp{e)7ij#i~cj_(;)TrUZlQv(&{tk{Kpc+*r_>R3yj<0!8@vhQO`}@*kWL` z&vV{#kvgl>>db&~kotaek@|j_R-eF_gB?}Yn>J*b@X^JbujOIh2tSp5O>&pG^x4SB z^^I3Hec>K*?c{Nm^QkW#%6nRSBTviQNF40u`*u|aU$<%3t9>=-TkSI$w;D}%QBI?r z#22;uhBf`YM?XhV_h>A2J)}g0tFwo1w;?bAFb&X`1u7UnZp8y zga6ak)(5K}Hsbj{bpV;ocTr@SoZ%_oN||(dO`IAsTFy9^9L1c&$vxve`cedaiM>t1 zDEd+ieJPHnJbTcgN>yJdhwg{26hl{vqbntVCy7o; z+zsW>q3CZHI^|B&4lnWU*8c9RvDfwA-z|H(BkrtVdrcO)P_{QVjXCAXcP3`Ao@>Yo zrdDMIJEGa%&PvvEcGh#uakZXv7&eilpK}+s4-X`#v&LW!h35mOA04$7IBU^S7XfFq z+9@#$b5~(cFUFqEdL-h8Mr)waEwgD88uiR|eoo}l3GfrOA&YInXqPP*J8bvHyRk7w zbDfFS&oV#n@$`1QP&Ky=a_tDtHD<~w%-k(xrgT?xfcaG-r_kr zLdP1Hejl*`9q%-%*nkeksS;lPRbuEe#{Q@=_P_aMcXQPl;Dbihc!rF<(w0tiUe};q zrE75hLFe>^=pZKVh1OESdp)Mj=>Rvf z2Eis4X<%$uF}6|07X31Azr{I~_Y$`xaY{>wQyQ$Emo8Gz57X+|Cj6t{c!+H*eS9u> zp9{WU30mYo_qAoGoD%d z|MgFmftD<-No)$;xvr)MqLZEBnT5!a@yLsPfrx( z==lKiRw-)>OvtZom0#G0(txU9{e*T`~Y{QK!+S zBMr9sz^0xHO!8d%FwN}4Gy}Hzz~%$C)h28wzIq-u>A!))>r^aJw@>^_8vVSA|Fu8| z{fA$hF-72WJNVr31o)gAj5X8}Q#4yo*jG5eFdyFGVa)e4<_8#a|Fz!ODr3Bt`AVJL zzcR*7aJa|(EwHXV|6B{qI{%!t+3I=ly#jshp}umn%-^by*ue}wG|uQ_i0^laUqPOO z1GhZq4&K#w;9afxh-XU)?)#6s+r+KUX?!Q+J`3Ec=Ymst&OJKSsLx^Ib{4o@1#TCc zxZO7dZli{8tlN=>+XLV>pLi|u9YuWLb_2NG2yRQkEk3=3XSvfMxSbDf{ouA0+}48I zMJ8^empS+RtPe+P&EEp+*4M4)T40g+Tj5}_f!nRGS?^teMdokSM~@lvN$7lSK$`=RrR)jTWI)8=wBD&SN!H0c)YR?zxd_D+v)cr`n{Nbd+7K6JH7GU z^gFc7De)1p+v)o(`o5OFucPmF`d(x9-F=yJR;aHJ-9IsYOCR>UY(3WkYt7#Z#(75H z_q=4ihX&2xs_!9*lL1b9THiN{jlhhbQP1gnf4bT@2wnNFFzSn5(bw-*`rS;wXVCAN z^m`WlUQ56I^xGl*cJ2I*^t*z7*V6BW^n2Ge-q;MY-~LOThxhcszt#M$;162QwZI

    ZibkteTQ@CWT*WsnPx})Vmhj4;e#7kl!b^TxrC&g+(%s7V%rv zX;X#bU;AUD{oy}KJ4LqzbpX3S232ZA&VN{z+lVjo`Ab&)^VIiG3Q_!L3I5|az~Thb zp}1?;z#H=ATOZt{@L%gD@ZXZP_Wv6p=gUy|+j#jx|I2YdYxs{IzIu?te^1C?%rEuV zKXP%yA%2dTxbl{Y?-M__^ZMQWGmC5T&cNcE!pthw%JM~Vc__Q5wW#b zJO{P_@e!XPqpG#m<_8vV@cjqT{wX>%%C;o3J#Ns~MleT)S9b_B8BthMC3Am6=Zw&v8 z!r%NEvjp%lzrtVnRfL2apG~~xZ6A!!*W~5P){nPu1I%CGLxz<;XTZn!!+<=Dzq=fZ z3+^o9E9*9C^BeX21%BB^4Bv(CrTF_<*i|rte)_PWe&{(^`}y#uTK^^ECu9Fw8O82% z^Yz|Q|I1?iwRwLxihtBpW`Ed6YRB;XwNGFd z$o+5B>`vh?7x3lVksc%XtG|E?*>UDqx04X%K)R#t`J1EIv57e+sYve3F4(Aa@K3 zs!Z8`1m_R?J9$0qum2b&q`2lxkCht#isg;lchqPMpa!-RqfsrP1$Kvgelk?Ek5Bt{TRWqF zW)**G&Lzbt{GB|$A%Ep=3Nx5R{2@i@$egsp{tNIe=Xt)2l5a z-=DVO_DB5D-^1{AI;G~7Q`1rg#-zy~%gyD1afxZkX(?~XT_WVl-Fj%Jb=8yVa%Fo; zN|JWHhMb&|l$KQW-RP9me$nymlGEan5>w^jB0=ziJW?}>jE{Zx8tp4x;q541kBGQ9 z2QK7~jwBe4t4#!}vP0h@V#E>~|D? zP(H@rnS=Fb-2vk33q12D%BWcOZ>{|#uK#}c2)jYq$j5blW%hCCmY@${AISD)_L1>7 zIz{l$bId>emH!$-vRyiCj@I7qD%*|in}{TKqnm%Xgz_os3vg0t;ljgD&%LJMM+ona z8<^mW`U0HTy_NEPvV)AjzYF8<`g?ne?So>w-X`DQhol?rFAoXK{>WFzbtz->Qt`dY zY$LwIB0lW)-{t#XYW^v{zuf!)N%mW7L zi_ES77r##^GH)GKemAsM=l^}4O%nZ6)CUfvFnlL{@%pI>p7{;J{J$|K{weAM7gE6i zmyg%~{anpoy?u}Rwcb&7gKT3O?tRwcC!X_cvCds6|2ZkN50o2x9L8e%^?_^9Yzn`X z;GdX(<}dz$H{^o0o5}jowb|(ZQvR&hGFP2N0PIP|y(q$h1++mE*7ft+A)Xzj>N3c8OxU1*hSK~u<#0T)N z70H;wE_e=q{EG_-7IiesrtuFG%GWIu{-VABC)E)yY&i0)y{CQ=?DNzFU(^@iBqbZW z62axow>|U!^zvJgkDp$9!tRrIFpInQXRZ7`z0zhgRlc$>W0pqy4fU@qzqtK|e1!~s zw3u9fp{G!P0pD?fmow2YI2RmX^mkvk(CQcQ`^jqK*o}@@#iMCE!0h91{3rtHa5*}q z=g(UHiu+5woXl@Uv$KGKuji0_spK7I$k5McDvF#U)f)a-j?jm97Qm!STt zYrDmA86&v_Or2m88epM zD3&i#Uw{+4N7Af!Vop=@tIqQNqk9-DS56)u@Z2IU&PD$W8g;cMg@2dhJ2<{OKzuQM z_wYH9RQt0t_fMI(pXGyr#eaoA2xR#=BYyF2Kj8gz633TP7_KuweDH^yd%{J2-}pb? ze$&TahOvIUzxEGsA%7YD_8F@E(Amk#m-`*w|KCgJtCWyb8R2hy5}BD97`B0H7d&3t41X zXj{rYEr%QJ1WrMcw3G5E}X2?V` ze_M#{Lz$&xwSL^aw(Dl9{FVsg{HsNJNIE%x;qiSQK;c&)FKzl^xN3jB{fzil-2@-c z?E@^};`eiDKVS03NBd-IcXkK-2J>(H;Uj-<@dP$IL#lh@&#oKg`Q5!qO~7UdWC%GcKV7aeM5+ezVff0@R& z9b@e*ZeAY&yFmW+!?kD%|NXPf6Tu(x*((0O`GRs&_(d+7@PF4D|A))M$?=&9f0^*N zh~N2}56S&wiX5W(OJf@Q*xdU9><-zveCvmleRBOt^S6qBBh60k|M8xg{U1eo)Acz9 zZ^*LGW61dd>&)zL5x+oU<9?KV^6fO*$MF|y=UOel87FR#`x~wP+nB$$>dY>VrPloO z+IR1e;}8D`_~369fAaE`Wcgam``2RmwTk~FD8KgotW_ZFl55g#0VSv~3FIQ#$C189@wx)LK+b>b zjr|n=GBLp(E%+z+Tg4wde^wg`f7E^2{#NmK3@<@`|4^|2Z69}BV=rs?9>Ok=)281{ zr1#Xszp(f&?8zGSt1_2Jl&-I>274r|H~bmLzb_{ChbqF_*;HCy@ayQ86N*d|AC$0-=c*7 zL#`c>PS)=W=NR)B{^=TJt$w@}QH`9R)cGrhkMfjN{GXq7AlG-xx7^5In!?&WR{ekO z#8zbc^RNGCe5>|Z_RflBRQ}p{{|oj3f2;WR@F(*A5&Q@It>SA>Eigxw+ETHG@~h5w(hJ}=r= z${1_o4;|4&IQLv)?59*Vl#iE%05VU(DaZXi1Qpl8fX~ z`Rn6j8GL-HLr-?2oW$xYH*f!g7x;?O!7o6LI`pA7e?!N|{^d`3{6~Arb~v#27r-6N z(@D9JUG~Ng;ymJkMASE8}jkwt#45Lha|A_i}(P1 zTWxFY&jbHB#K^{)lT<@mo|c^*V*$JD&DWsjfBu>{Rb{Eq{H=?~JX+@(cU>^G`ir zx&M5et1~%&eSr`kV1KLlgThym^?TeQBY)W6A3pq^)0Z9%S^c56|6_b<@^N;DJc4P+ zM+1xhqJ0|g%3qVR|3<<7u5qkhbWXL_zc@Cz;9C^_Z6SUFSI)`aH{KT*SA3aBm8Q-%sdYyOy!~1rm6oWV19fuWmfJeWho9)K264jXWvr z9&!GLsGo_%j+EDo@>Jg5Ikfpj(k`aCu_1f^wWTq$k25!)zj2KDPhAmEI2Y~H<4GjB zKF$omKGFkbA6*K#I2G})|2pd%%Kk19|2vKk2|z(c3+L6+Vq!J>hjII$e7$aBe^Fn6 zld20B7F;=TK*N^>{0I|#QD1+`RgM+;zUF zXMST8$4_p=>{G2h^M6NP?*H6AV?;plTv)(yYm+haH2d2G{#RlDbQU+=0ouPJPY<%; zf-c(lgWkS+ScCEJ%;TFBuXr?MK0k^*l}I}CT+H8*i|W1SS*Khj;^&f?>UnTWh5rBNYrTEyqcNWbr!q>T^w*yrKL><%0MK1kx<2m0gh;K1lV4i740Sf3aD$B9k20r~j6 ze3IGUAHKrzA%k*!^s6_2*k=LnUrTlt5o`hC%N!q~U+EtEsPE&Jj%WNau7US=R{1Zl z_PSwxCGhW2fZOLN<1hb_HT&$@*xpXrXJl!H?>@}hYt8`i9o#UGb=Fk8>Wz>7U7^+7 zK72liJ3xGe<3pxKI0|~>!#*=Of9FJ=zo!DT5Ap}{>>t_5^=n@Lk?}|W$Pv%RX##DZ zga5$nn_U$D1Kj`RdCdOu>A?5{7t)<@U28w<m21gW|t|#|Pyyv%l?Z zVEo}XkaEX1r2h;M-e2hBwidrn?7e2sB$dBU|98s`Ruk&=A^(0E<9(9JA9zFV=p4L> z!Vh}Ih>!lS{u}|~56Rn(Y@g4*L*qN&v(~>mxNJ5#zBf&@Kd)yTLF_vN6Z$^iFVuMd zNAmqnXCB`WAFw|Y-U9@Rf8nKL8I=A1h!f%u=WTU-Cf$8X8z1oTpIU#=_*U~bd1o;B ze&H4$KY@S3?*8xjqTU({{HN8GWxKh~2R4idky zfDc@xL$g=cY34sbhCcdIdw;FlAMNK8y!~n8^M&03<}d65nKmiUM#?_&G^2f#zN{T* z_5IYUqPbq7@Y~#>@sW<1eZU*?cE-nK`F&g9Z|j#e{?R>ee?jpdEbs?DWT55&Ba!hn`Vsce=aiD~_ zRC|ZC=rnsJIUzc^8@nD8A7@WYi;7O`*Mr~5u0$rrMllts1p`VMn3xuqV6WOYDJ5xO zT3n)C{k>0IJcA6EU-)w+xpz`hd~lAY(P`1CaYOBmJZly89@wXTnUA7V)WwX#Ylf%T z2ZSd^C)gXt@|()YAEqWH)-QweaP8mWi?U z!I~X?uxe!Tio;F7(l2;nNv)VS#3dxhS4~b*v(BCpE*sA(dro7LV(lr-T<+F`IcHQ{vOQk)w06niGP}~=9`jKpevDt6M90V5Qvk*n z$I?JNt7NGOtL`ONP92gOquyAxLsCrt=-AklmWlSXT9xFAo?kQn^DL*OGt)8j19PxC zW?;cNJF4X-H7zv^0kIXb4lJ(l@eg6Ce~u`@3)sf}4ds@yLv zExD@pUkw&q)WVb)r3M7Hf?4LT9aEwcQOz-%J-4h;b@6+&&>c~=D%?yq&MAL$hw|{W;7ox?b z#`TW3`*N$}<82?YTXi&c>g8|2xthSoQyXVGFv6`KA~h>|%1bJ+GOu{Sp?*cX3g>Uu zm)_2wBS$uI{`{k$Aj!5fL|Q#pTjN&JzxyArE|Bw_|0z}UY8!C+_z5l~Htms2;n(^_ zl5BfJqz?7j8oFUzq2;UUy4AUZ^)!G3$(toOmDZdMa^?>T%F#kt-|&NSL6ZEdIevO- zc`596865`)BL0d^hsRL(@10iL@&vw0U2;temKB8-g^@V=^PUU~V96#){@>2RO|M>G7S8qk(?^+xrIbVQpo1*4# z0Qi5rF>gJEUuTl)2?x#jOOsXnoBsLNdG-1f3jeL&RQtaGKSRY&C-7l+WOHnbyk*t$ zl}9YUokD{o$B_`}W))-k{Xum=lwVB(2U5f@d*kQNDf~QlRR3QQ`hUiMEo#MW3ctD9 z0dai)f5$K4y7w=I|LC^*#H(J6FMT1Frp*0mV6S!$%f6)I`^3-BRr^TCVE-UJej>gl zug@;o_5meRiuS+pd8xG&|H~?WrD=A{_7}_Vj?xvnQ~tUAUrCbx2$34A<4oT1zd;8{ za_2RV&m#ZIJtl{#{IiSx89q#nGp9l%cLxnW(>@I&)pV-_OUl}CDSbkCreXMzh`(*+ z>3S6YHn*y9+8p1d;@@iJi!a)z)WYpSlzlD~36fkYevG=zt2ckdPsiLm(!n~yWDu2- z$bWwMGT%`Cvr@H>GGUy`-=}6RW8H{{nn@ zWSEpe-~$&a?%L#BPVujtthOKi4v`A&q4`HGQ<=^MTcG*+&;JU1u#U>#%Bp>wDt?0) zBR>379x6-DDsm<;KT@kt!(YMk*}tILZ~IjHNY~8q4-S?k`I8a`EbTCZ zr|{>zt@bZ&n&Zpg6qDo*{_%r~^&Umx_g44Mv)vAn7A&Os%e!PramhaXVHf;RcgmtJ zRQ{e;_g{3}36UDA{S_)cqm2k0ND*J2UURUD?-QT*#jEwp{|Ee81!tsC{4-ShDEC98 zjoUQ#ne7MU@2v`DB8kC-Khb|) zEjIRJDu1UJSMC2KL|XTm5g+^=P1T0`#L6PPPy1oe$Y{;}KJ8B>T`P&9Qv667KjLh0 zDS3TyzxZ!li~pIz&(>Fs{{%kb=dF{)C1r(w{O&PtSEulwJyEFzhe}F+n!l7@P|EnQ zpkMyO&%d*V!f*9djXx@W8x#BpS1~F52jB85`p;^4auS7~tl~RDLZw&}{DaqvOU|pl z_7US}Xx~De)c6@Jj&EFBq4LijDot0Xvv~VI>>n}r6)7r8T&+~Q*Z4PM+OWY?d@gZS z<^NxHY34x2C_%DS@?|et|cN7elqCTu*B&(i@ z{Qs$Nr69%M{j*wrbC~0s#WxXu@*{aKh2Ld>+JDIczGxri;DjZVeVT=-Omc=w>w>cx z^0%LA63zZm_1}e3c!Wjz2xcu3V-%Uh8x6N ze8AP;znjbx1TN&7TR-QZ@PGW;i0_!r=jUU8lK}B&@2-EI!apnEOIgH!K9Al%`-+?K zhy7hk-tyQR(rg|a>;gG&YH{-W&Dg(<_>SfbA2LvUgZ_w%Rw9Ta}8=|=w%zcVwN2MxPJes=%Ey%hc-?*D2f@5xsu z_e(rSp!m~@-y`RT?4LmM$9#oA@hdkev+k)Po^EylND?JjBGANkCWy?M%LAH+AMFrPmeVEJ0`(=2j*%g%EdzV81#f8n2j z+CQxQV(t3^AO8J+qW#A)#~S}KBR{P|;cs>r?E}8fj~cMt4A4H=#;=`8;ZN1)m$G!6 zk6B!EpNzDGe`2~_+n>e9J{=Dm`2*h;Z_Pe0|9+C3-*{ZG5AY!ajX%llMk*Bll@}R* z#2=|UYySWr`QcTxJclS(kZH;Br2qWL?GJp%qNW~w*`odTmid5OKWCU=f5Zdlb!++C ze$%b~lzsMbe38FxbrVbeb0^=|pHuj=uL=Ic%dfi`|Gr=z_ah+#V0Xyb=Qg*d@YA0g z^Vj){!jR6hdz<;E#tl_^>VHw%} z8CuVX5B`vW+UL>j&w5h))3x`%>L2_ExR~b{DE?bpN5@k5m*N>dhLH5~ins~Ma$Wx) z{YS`pLvw217rY$B`$r1@e)gd{Zs=N)Ihve~_5XDw`T%|j8CU-5U5fw3BS!n%jknyBAx|1rOT3>3fGFU7U_)jsk6>R=;(tPc+vDE^I#7@M|CJO@4pz8^?$L z0~gYbc9xI;ctakaI_)TBALn&}Kld9S|7mMB?T*cuWOL*^GsyS^-^j$&L3XxfC?-|L z+oMzM&DH6`@^J8iJo@NhD#dTZJR?87{9m&ce|y)RQHsJpF2rBtm(6Jnf6AIqzNYZI zZ)E(Df52CITEj2z{>xi^XL@Jq-q8vRI?pW}jmg1=S&8J^ZTg5s|{HQL{q%Gym< z^LJwZAISE>=JQ5;eE$O(X#5@3bJ|*p|Bef+?nC@_&1>X|RIop$9+6L>g9I<W5R=--s zf4aWjL<)cFX=DEC_&p$sMJ%HT5)_#}a!#`y?(gP)b$Yh}Q@Bh`8_bC2_7Z~wj zcf>cG`{Vy!rT^`w@ZaS4qJ5;^tepuv`NOZ+v_H9i%{0M2$_`eptlH=FmOKLf@8Z|sd@y{EiG5bHI2S1Xrdd_DQT9pQZ-UR`TcG%L`eYwV z;SYRB;{z8`a37!gtIzUMzI{C{MO;lX&|<%wNYMYxb{HFOEd zU&*k>zkZL)6)F6DXK4QTPBT#dnH}5w1cl$;r2NY3csn#e``m5Q@m&i4O*8&cil;~g z%D-340w;xEVl8bS)LVh#cg(Urol zY=ZB$h<~)j{<#!>txYt4nUB8&YM;*KhLY>!v@_watg%?Ws+8+U@vmbNA6#p#;jc*V z^d*Jg!wi3^HT*_z{YZ{ajWNTw7=J33j(kS(A8ZmI92V^p{z>226nnjxg z*JkaXjn@3L`?4UY%6|}|6p>vZWRB&P4Jx-KC^~{-Znh9Y*2TG2t&+tiQS`!;et-N6gCCDr@D}X|jI$Qxklb z#r{{fzpDO2@qhgooxjp|*7(o)vNGBJ{GG=)y#Is0yOUY`M4a~5zneX)Qy+?dpS;HU z#Z`pkPO&zA82^5b_bL3ED#X-x|j35B{zRY+j62{IR8eBj+E?;qd|ZXm?^ADvUD; z=J6RH=u+{s@f82rvyAzReAVT!z{annN^{8iNSoW|G$@m z*&pMFHXfIq*{sb^db+dB4$40Bg!q7QH^@NwPx|w0PYSOj-Oe4vt48D^FZ-y zeBtgv;eU9`h%fET8h*XD&n8g#ef9mZSUTV@7qJ$fdoTQKI)(q6F#e|SdBM(Yto{nr zKK*KaJB`A>&c_c?zL2l(>(=aFp}}jdDEz0KzfXMlhvDmfDg-#heaIH`Zj$d$LsuI8 z6Z|3FR|0!KSbynfEh+vF#P=heS-e!Z43z)&jIQS>{PsJH@d3Pn4=I?(C;s0Z{x=yv z$MF6g?2q#6t`x~21B?%KJO0_2;-BvV&EG1%qtvh#6#g3C{{nyAJ|lR(2FSmA_ZsB< z-7`GC0pH2nBUa}Z-n;+pa*BV2wnqPizlhRb{+2A;lWgBswHxuByk3DsI{xtIt$9Mu zZ)+joyE(ot18l#g?Vd#TuZw;x@aO#^^zQ=Yzj0~PqLlq*^}9Fi5BaO}zA3~v)A>o2 z)wR^>)M7J}JnM9Mmc{a{v85i>lq;(%v{hCAHE*h(*3y=7t5eH&e$q;5Yb}c3exdy& zqg}9#)l-4S_iOf9?J4{`n~l%!va@mnI|Pbf?DUPv6#mOo89w?yPVNtof#S~|zUl`G z|F?Ta{wV*BdDiN`_kR6fF$#YUufOqr1?@h^QD%Qgf92=iZyU+>Wesj0)UU{2tM=Ks zDnH5pj{(d+;166k%4LA@@#}BOXzO?Syq}xG>sOQ?iMKzj`p<|VrPC<;ilF!|yQtCOQ85+Fqmm;Xjbl{lMB!rS}{q`{xrL8S(Yx}v9_(1WPTJFYVpz-bT`DLX4SJL2|A}?d1I6!m z=InKne@>zQbA{O*aWqi;D-S~drSLob{F28Wh!4(hBR%KJPHXM6Y%>;;{fA5HchlOR zp1+uP#-Afl{$)QpLe5V;^fZ?TAN-|3jXmcsi~QFvxIw{%JS;N*LtKM&ZhTJ0hlYO{{YTEt=UrOte-2JQxu4?y z##v+eQh2-Gs(;E`Zrr8t-&|$HNBnf`WX}<({~Z4C8reS#-@@?G{&Defcirv**1vhr zR%l4^kK*{qU-Sn>nVGZf2>A**MR}F%e{S@wFOpfl+(P>v>G;E+6ZRwdesqwN*+=9r z^KraD@hf$%LXIz=5b7`G5X)EKLi*!Byj^}WzCB1{Iji&knwML?4-MkAKm6Bj^*&DJ z@5y||_$EzZ{IUOl?ngob$XCeU25cw$=e=(m@v$$wRs4E;OUe}gsdtU>0rs#}k7SGk z^#Aw+)5!6=%6W|VXqWig#~OBlY+HQ#Vv4`qiRCZiGx+=4#}~MeXZycSwhvzoq4`_I zKTz_U%M||+eE%qszrTHy!5i{u`AcN~YcS^z|HuBl{`Lt5E@YDyc?VJa_jCUNKK9iW zrQrTP?dRcpE0Nz{*1lql|EO1hA87oK`fU>V{bk;J#`2}_d+TQ(T-XJ&_)7!H_Os1| zzn)HjWWgx0Puz!`$^xwsQjH|#vk|r@*nYIMRNbKNdkZDODjr& zzK{Q`u5maY#s3Er{=g5^e};eAl^p*mHPBc;I)(8&oJ*$TQ=VN6lKS*fm)iFXQl;4Y z#M@)is&+_A85oo1+1bD|VukA+ha}s5$EO-zDfcb){0%~l{*C>!9DKi`K%alv#Yl4f znbku3Qpe}}r0MAh83@0FJh!M<5sH7#`$qoQw?>o#ypMl4M%8Lg;s1G+jxWFu)IKHa zZc!-w76O0lTO&$;{8tZ~O^)y6~9}pk8Pvw*OC$)pZb>ku01t1y02aKtkaJI z!!N!iH;<3*n<{&@>rnSYh{^+!Fi_o4!-Gig<-)dLP&WtBMw&Y|iHcPB1u>qfdwSqUZGVMuwxL9&;M{5u zPL-oJDUwoQ};DecOucU5Cuo- zKwxt3HfZx}9-@X#gNz}>b6fCBol@;79pYl!cp$vDmGatlX`p&fuhh8MaBWwqhT55` zfkLVO=(YDr=C(bpGI90&accPNSfX#&15o{fS2_3EzlTEAw!t#)s-kaJ0#yBiH|k66 zqX%_RH`emCfY$~?Dba&e0}Qn@3#ezB24M9cGc6!>WSOPDd8=8&vyYM{y7ka@{ff1x z##C*VI55F*fk*U8#!S*~YpUL_M(DJp6sc2YxiQA~25)H3X4vDVx*UTIxT4kK6|b#P z;3=#6W%je`t^5z|7Ra}6HGGsaM~*sJA07K|eDw!c*r|99=B8<5iJ9wohO|-FXDmRh zYP%f?L@|IBrMr>MiD?j&`6^SbIfB0T*)RzJ+xu{IUOVjl7EI zfbDaWiH~)7-Hrm1_78u+g?zr{(0U60CSTu9$@gM>Th0QK_78u+h1@(Ud?AIOSKYrw z`*Xd)>|@6Nmb$f`_78u+gfF=_wQ5q+Wt=3 zpW_2IpCPOGE7~sUOyRqE`E?oW<6FKW{wyGA|L_O9K(;z}dJcu(NzGsF&vxklX8)!) z+clu@`*M8g@c#|}?$$E5DEulNnf?DCZPx)GMG^IfBs6IO=|z^_0tOJJ%7!K)U<4He zkqhB~KuAIoq^NL+C^n3OBA{?e6AK1Jtmt8(2x3$O3*sR~tVB^!uzYWJ=6_}H?cL$` zeJ{UvZ})Qhe{bHrDLXqm<2q9G5MCoR2e)Sz{KM^?Ak`VoD=FOaKxoF2g8S4q(E z^S`uzdCf_0bNGX5DtvSQ|IRAovCH z+v4>bIs8UHsViL7P3yo3-`sbBknBXCi)!Vl{s{yy+Q%!U|C=TV-(o#aV2PF*NbY+k zDOj_qbJz`8W9pjYoc-H%d>RLof9(6H!9VGnS_;V^;0ENj7C&;=(}&e(>;L*wlz+sh(eaZv(>`RxAUpoY zMfYC9*)R9sljPU+zuiArN8Y|px*dQ2%7UF7{_Q$`uwJJfe_=hUyJ;PBF_&zYtjUhw zY}@76aQJe6KS}0j)qdIWVZYqxPcq1ke`@rQz8t>X-%pa)0Y7w~Ke^W*$c{gGN4HE4 zzsF@d{=0M{6vo5vU4}4=yOCcF#$1j-o^3@!EVr`9oHq<}C_~}bPvh1dQ&{RkU z+3~mL^=0elm$d!ptQC4=nvJs$D2=j@mJPf9Yz@bwS!SMEb88Dz(Q|IoW^{b}t# zNf&uy;LCj|E!GO`_&dtn@C;|a+<#J%KL)+VdCf zd)|{Rg=CN&|0dtUZXEuP8sGDIX#E*^f00gYuVj!N|Gn3@WpVhul63xl!Q=O%`$tij zB=Vjk`-wqz{BK4!9M9oDIZxq>I;M50-9I(&Q{~J2r@4>}vg1E?`G=);cR@Z1$_`E91cUKoF=`hmlj_b;B6RV;SG?_2p4 zdw<=+tFCZ8qVVnenC>n|Z8J5(C0l5lR$`Fd{)}eNUeEcbY+2=hR|C_!)Q-QaL457+ zgr}GG=I~$Lqb%~C48;$|=VG#7)}JLk%L>ULyZu{Ee0U*;zenSXhN1Yu{C$lwI-dOP z`+jRLhcEY!m1N$eGTQE+tqHt;tRSssY>VQ>?{#MODDqEj zJN|FlKfW|oKiK_KOSg|Qk0rrgd;Y$2{i|&M&DuYfF7k~H^?xva(7%=Y$Vvv;@msf9 z`v{l6t=;;H$^ESZyZzfKS;}(t>4g8`{dbP#@b_J>;>X`Xz0YXJzmx8m7N_jL-jf^||ju4*&PoI)6(|*8hp8PZzuRg2bA4-zo$z0s<-Lu= z?_O7zuhyn@sU08vPb0C7kPL#|kUMJkT*Tqa{i`MUwg02-V=ecx1_2kc__K%J;qc$| zt1HY5mA_8-avy8SAmBnaOC51BhyTes zdA0q~_Q{reWrKhV*}KV;?D)^sN7NPON)?|@_^FRp5|Tl{g}iJ|KHI;N`)^w`PWj)C zkM`Hvhx;LC`+s}&6?XhV?!PU`6$4-H!z~#EyCHX%99_!!f7MmW|3+PvzgxP5@57g# zM6pZz+oxBe@UwgL#?Z4Vz&c0Q8B)%lW`lP;>-fsf)uJzXu z_ZKJ5q&Ma4PwA=fJjay%Whewg$NzEv<-M$$kPL#|kTr^WrE&OuHNNqeXNKcKb4Y!QbroRj=Q)MP3!3c6{^?y$7la$pKQy4xIXrzcNEUf4BFaN0I%WZNcZ8 zf#>h~dZ|y6Q~w$M@wVT~j-OvuPXYNx>H2}5u!i~{^@G{4ypRk6F63Y9U%Z*~|2H~5 z&1tHAKS=WxLh(^Qi*4i^$(n!*`D2~aH*omxA5jy_KoU?-Q#2nQGOaLHl2!ugw)n!aPUuNeqHtAiZ-( zu=hvT%~trX9?E_veBap%gk%tKA@|ll%|3rThWfd)XKl#rKgk%uxhU{K^b{dC2R^xjcNB6&nuqA_l3wgLq!(0wuyr!?{Vp<2vKf(K# zu1%c@wy8an$YkyI`(!Z=GB!gf#RYBD*V@;m&YwD&mtb2|RLO_lw{#o_pJ{8Qe0C0~^+B!l1=$XbVbv*YK< zaI@%=XZt2)K}=nzSFr;_LWvY5bm<;rQsE7_$u_83bI&z4tx-5Qo24$B!=te#uNj zNCp8H^4yR=S8(`O9935siK_il_6N)FEP2cL1J$4N8d>awKXcfTr#SorIzEjF%KuLO zF=rVV#?1sGW=$+L$``79E)11xQpG#eSIX^SCqhYZV{<=#RviC2q-=OUGo2GTC-9OVx zsQOWAOv!>sEv2R~Uegxc@yo>=M>L{VOT^eOH() zKIRvdZf_(cgWz|_vtNv9z}bJ`Lv@AUA%9`NXNDmpgMbTJ{ptA&IQ)vo)fMJM75|$spiD4sstn#^Eo!T=~Zv1K(KNKu87w7qY?LVHa@t#Twt=Th$M#^o%j| z{`54pzvdZw{&-;%i=FVt|F$BP!}olouJ8>utphv$!Vwhz^!&7_MiYyj@CUZIpS{1{ z3HyIk_wROm#E*wMijqO_3*^BLN%wH}&sn7W?;2uSmpb7mH*6v#gMbS;Kd~=6{(o58 z@A-+hAL9eX-I@r=AmBn8Nl(1R*+0&$>=%<%e6Cy-p1+7s<6UaMh(W-G>@cLHDu=(k zy~ZDET9?}K;eYdas{e^Wz=gaibr?Ip_XOhSl=4rn#BlrJpY#C@gk%tKAv^YXZ8m5B zA#J})`^S!t_8omJC;T_I{&R}M-qtx2q5Mi2DEO3wK<~`R9FYzyGH+>so~$?4MNVK;y5^ z#vXqd|Iw-W9KQL6GRb_;WbyB!`diM=jFrD__l{@#|DES4eDA4H`y=l!V&SK+T6Ti7 zzq?*P&HP2hfBMYu_(%Ej-&;pW1|eS|Yn@qE#Nnqb(e~F;_B-M)AbesFa3M#y2DA15 zAj&^Up3}xcsd5Iq^=kQcC#7)qyZ=;I7^!_i@PqMrawLC$4P3|vo+7BLTKJw;eEjE$@pZ(0++p^gGF#fD5^3 z%i}{i{AnlD1x6#~e>?u@0jmB?r}r2ngA#C{<=wu2R?Fu*F1>#{<}aP4`1Bl%e7=M0 zr9Mfq%GZE%+op2%3!inlFkevlyJeB|PZ00ksw#iIU$qsILGTOY(dtLs9RA;kpDm&I z!T3S{s_1ZAAsGZ*$W}LOVDC>~(f)VcXj+#l`y=u5zHTcdgMbS;wr!7}IQtjt`p2*1 z#|hulubq$#0xslrZ`{*~!%xmv@$Z|i>K{k^JK72lF$lPjYkplihQn|3jJm@6m#Y8m z_#-M)-6}g3{mFL8n(Ar^{I#oxZ0Gtv65sqQ*#C)!Zys~DfA0FEt2z6Bcvu9iXUQ- z-TuA{=J`1LCB83){lIs9<81#;Q?H-F+257&SCVIsX-O`BU1Yzwf^3(p32_7YcDFy+ z@v&M}b^Omz@xNSD8^^ct2Tl{QQi=FWE2X$lT4_u@1z5gozi*4cft!hx(s?q!7 zMRhC&zTN+8j(x`ZU*enp2ID6he$jo-{ll)WzWf;He^aloXiir4XU`3{AO1-_+)zjc z!S9ftSLn!&pD$gd{BJZ?{&&Ln61HRza3S-Kj%mr+pDQqT=TrYA7UwfD5_Ryo>E0W<9F#jWf~ly`NM55`%yX zdGxi~8#wzdFNPlIE^V*PXALN5Dq z2RlAI=r47FOXClAz)$rx5RyT_g}m@spF27GuS!$?F=wm!EoX*=_iyF+wx8DDNF83o zVki8;-j?kArLWSp{~hoXM^pc^4!xg3_>w`e8}gbm7qR6xPy5HG{WIRdKP5jj6p}%} zh0OcmW48ZxW1hOgsNB~&czu1ie{lbv+PaaD3<56X4RdZ{=bvmFqx@r}q*<5VLE~BS z5A^}?{pSoJ83bI&8l!X1a``(>=dWLvucDEBe5U8t6_P=~g>1V0?n^lQ1F+w1T9=ML z7;Zo6f3vWzkPHGYWJ0Q&eSY!%v+4>XTa~ZH{lo1?{hYd@mXHhrE@X}OrjDWhNyPlX z;)wGKJqu#_}hrmg$p_RM@~}~dH7rS;dRc z>m_fY_1r_t?<8`G9G^_@-riy-|0K5C_C05R_nQ>HucE4-o$yOC+6&1blr;Fi-wVsj za{Y6O@2VKQe~*UmY8J!?F68BtDp%m_|3Sy6OZ(^Uzr*VX_^0G_ijeF?PkIx7Bm3!X z97X6~weCdy+uNMS_wGBT{D<;}>+Q0Cg*y(&`46eK@r{4Ueh=Y`?xuB#_v0R6mpb9Y zFOazp54fH4&$NFOoVSw7--*;-hQ`m!ZB+e`+~GnY88lNLK>kv8=;iYLwGuslqoixP z^$Uq_Rw^s79_Y0dmC}( zD`QR>;iLR@S|6rq5g$MB3*_$!S?v6lquM{-s>(lqf5W$5#mDLXml#x69{~TZj4Rf1 z^_#>utCkhsf}YkD(eU&31o43jd1aN2?EHDrSEb+Rn#ST^n@{5(dlE(IkvbN`e&9kr zyJ{ml|Dle?_m?rPOD~}QP-y*t_BZcvZ6O&1T*zb3-uyThpZ6|Q_L~dU{k0RmabImA z83bI&LG`Ax>r?%%^VgGLT9-QEi?0YQuh;y57<7z1}W>k9hu7vF^a*oPQ=QSC{y7|6$?L@b(e?2eVffAsK{xh3sD| zcO!>iy@v9?(Okv99siG- z7mI;!;6TgfzERh3?eAZoQ~UdXbNhRVF5mX{J=KPPWy#qDiC?-keEh2U7gYZ<_~rEV zPWS_D_*1T{^o7Lt+VBSxz9@>cpI$4l$O#{QhrD6sPj}J!+<^?j`(I1^%Kv!3%Tq_S zkFM-A3*XfJz2pidqO#U01#lscKk$7qhkxBUg^%}JJ(nwdW9s?gFIZCK_ty*V}=0mBUgz|=HVyg{%8^TTi-?ZkIRDb zk2rGWEBt`}W5oa7<{wq&@cUh>>_`5J2D*MIu;It3fBL>$EcYjhuz!Y*Ph+3z{|Dkn z<3EM?r}&BHpIJ|y9n9hHx>VT@e8h{lP}v{AC;Ow}Km4yblkg+rGxcAEkNAZB#yr(u zN4&<0pB~wLuHfwdp}E3`|2>+W?cpb{uO_y!FnZxh-lCnRMYE4O4CkB3s*FP71+VN2izX^?3NwVX+vQ>Q-FMgS} z-nTjb7rhZ~x42QqsrLWCp9Wg`lPcsztv_4sdi-h*-+Pb7zgfkLQB_Irj5G^3P(N2F zii$sLfA1=sf85_I{~$g+PxiBLkb_F zLpl2^&Qu6kzZdB+ZcnqI0{)57e?GkJw!{4d*f5iI0e(itP9X9+5BctNCp1Y$ThhM7O z7vTH4>vFr`{P3TdvZNY^Uw*Cf5Afk1za2k@e>T18ll`BF_Uk8|ze2B%>euyz_fBR1 zNbUcGGOeTfXHm}Y-8uZ$+J4}}e%D<#{A6!b{O?M>-NfNv`KIy@aD7ke{^vr4AE=*W z)NgkTUwxFrPt)ZqfWJc5pXZ6+>*vP%IQ+hQREY;Z+DGr*Hv40=FXf+qq8W!@Wl6Yy z#AB-daouCXx7~lCUV~hB_tcJrA5p(u^{m22y;a(huiuOYG|t*swRQ5hz=ago_L#!r zYx{wp-ipUhr<)`Bo*W4ECS=M3l^)^zGg-%{_d;E6_5H80Ncra}+~38x|2^~L3vz$? zi1IZH@sp(bRpKhukMnE%K>Zn`f9|>Fkx~x-^i-X{dYr+fpA&i?Q1-`*|LUl;yE*(# zI)A+lP0O_yA8-{ZeBY^o*7vkJK-BX4c~Q>o9R7^QwEeo?akbET9#r^&@@tDvPkH znN1(?JLJK;=jU+t&v;zfk8vMJV~MhVA<7r!Z?y4^yKmno`wtQJ56KDdKf`|Gw2C9| ziawUfM!qh8Isvd7viIPt@8RrE@G1NK|0sW$BXs%I_6PDeM)`g8-!|`a_IvcWA^QKY zA8{IZE^OQ97Is74`1}#se~9o;3;BSH2;(PaZ0AUALF4~T3^x!a3M2W-YCac zBk<4Y{6+kr{fdySXku9i0T;6P*DIdn{GWT3@;}-~Ums<^>k$<{f%+jv`K|cjTa!8a zSKg=aVL!%WBJ9UFu1*8&hAgdndnSkf?EG+iq~m$b^zhxyAKk!e$ex09&mL6C1{JsC;1HW+i&wLPWzgVp759op4ZSjwO1LUW_ zr^)@(Bl;&d?pF8+y`qcx{)2pt$p1*N{{URbYm8_A;_!Ps5{_T$fKQLjh54W}{yVAn z%l>DC{atB3Y$&Ct^7hOAkH5AvzB~2w91ee!&HiEse0K+$$0PS?(S*2%-1B^CL&A@! zAH1(9`w>4*ac|rjJ$}CYcA)HkMzrrQY5VmWh6UcjNIztvLKe z_bdB>5BvRg{21e(OCKnz&*4|OLE(E7mA!s#zxPpf{!|k+K53Mx8?}A>ZTu-Y{vY9= ztvxk9{G$7}u4M{8P=8YYE!z0xi(S9JjqoG#_m|}G`T^~`T_Rmh@$>ziDRTTO0{@R! z6+Y@WJ1)*+;ZNV+VFTx%*}8n8zA=ZZ_Qk*4=ARhti+k@KCWn7g#}Dvb-|O+Q73YVa z^Wl_UoPWHRs4^Mwk6z#2)awEHe^B~j)&E0YS;CfIza8I(i*_a!{^nZm+`!p?;|Ud? zsuvo3ehOT>bc&yQD{ggh_+}Yf{ESfbjOieq@c-Gevn7YWLiexWe}5aDuX9v?8TQBW z&-7-G)#UJte^UNI{Ruos#68M^ll@6Qtp1enBkG4Kmn(c%et5goiG`{P&eoo@he?z|kvTDagZqEJ^2Ylao z;=jGRfgB%>@XsjiACzB|EBGbgCtLp$c0)e)bo&=L{3}YL``>7w@2g|r_o^~9gTt?& z=ifW!uj@w@r?K+4`5yxlIQ$%KzuiBuJ6`|%IJEc+4*wEuza1ZT$BTd2sG}1&{JV7f z;^?2TeN+LD;r}l_J9r0&Kj60*`FnBSxbO$we9`wD{$_1|gnzW%@%rcbO2u6``~q#i z9pBJ)$BTdJnQzx}_+7t_;h&%4i_cw)PqY1paT-6uevKQi{Tr&^Ifk=;^q^4EDTR;q$B+*16UzR0{quC& zTc73dr`{57zrTfQ4_!|x{D6OA^#3b27(9gVBia|Qt$e|Lj~?fMj8#8Ba^uA2oc%>h z!|nIgQuce-oS*$KmHU}}{*|Hq?;Wo472}ewwF-Y%bA5kWA^QBT>b=jd;{0>%tIB?- z@?~sM^`}3^_~h}j4}Q$ydv*U`=-wsD8QOQ`M=bxht9&y%exCDyvLE#i@a-}d{^t5? z$8z@Hr}5z*JFZ_pcZh|5bGrX;4u8wRaR1x!eS=kf5extE+K**%_(_MO>v*RK3)Gi`NxbCf84j#7jpQkb^JTwd+yQC6=L)sQa5F@^WWC$`o{?$`4P+idv>R= z^Cu6*$X`#K{@L8LH#>hg`K)TcP>#I1zi+Hdv;GmNKa+~1jxUwlRZhlFMEm#@@HH;< z_%(ju`AdxXCw9e;l$PO5r3 zaKB-TAKW)W&N%gfe7+WO|M-}$KUFRWkvBl$s!}KK`xFgY8h-x>T*wQD_Lt9RBk(Uv z2ZF*Walju=>sUmaztpe%=BK&*eO=#Q7)_Pmy%kh>GB&9A35-9KF`|wSTztjRV;uf! zdf!nJ+AXvr-lr9Qp#KnKe0WX8L5YMP(Y~jAs{DiUE56e0FUDJS+(NIiCi3~(_j`ve z;OyTwLE#%N)4CM#;(Eqre~kHWKVQ2>_TM7xulv4=PuTCD6&{z+7c2fR{b2um&i;d| z!tFQSQukZNvo`w&7A0T|P$m@j)sXq_dQCa|H@~OwQGQWhLi(PJuYbO+c~|*-B%=Pg zwMgOnZw=2^zqUUx{*xSi{?a4q3)uOk%Qe2ohVOk&`6pid5w$YUarW2oD*KJ{s$8M| zbZu1lf$|k&{@6FghfZ_&muY<9ihO;4@w~zh)c-O1&ucEakR4w=ZoB_-8n?ywRv}^H zXZ+UfC0zWhdRzGi@edrtkDkZ+yh!-=zHGzgXob?DiI`^FaL>e!2 zleXK)RrQ-+;|KhoS{!wL@|yMS<@}Hc`z!2K{sBJv4c-@&{Q>+K{hv!Ne1)A~(d;XQ zkA4rHBe=HM{1apTRn04kuH@{$;H0)6?csi1KfGwek1_sGb5V8+4nL#3YQOCLHNVD> z*FO*K-ZX>5Py0#X!$0CX-41NE*>9`g(654gd|h2xz9RZpMIR~upk7H$SNQlAvJ1V3 zFV{f?p~SdMUd7z=d4xn>3v8Bl-{T)l~jL{po$6pM{Tl z(|4Ed*Xi$_?5}tHqIWs`8Q<{u=$FKcpFKW>9iLfJUE!mCcEq*y&*2wHF=ds^-w6MB ze-6jTIxzt~8{!)uem*_9h{InqNyP;G4}3_}*Eo0b|H&iY{>tGWw%ISvD0^W)&SUit zXC4_Q`zI0pIcc*$fT#7q{=oM-0q{HIh%1ZBa`+8@3HLwNp$XWp*QJSN|CZ;*yvX6F zWQF@5_>cknp~u1g9cxF+=d%(18D+B{>(Ib{oX4{NxlQlg#o_1K><1ns?8muN{4Bih z5_bHs`*Kx-dC#f(#*?h-H{)e>p4wK8AGWSg6m@>fs78I{{IrPnaRKT#jqlRqVgB>P zU$b&EYk%FJmHk4mk0IPD{=Kg#`vd(4Tm6r?glxH^#RZ)Gi_w1R`(EI?URC&k_AAEx z()qUxX~yBdI49hG@a z`6ovIwoz*9OF8`4ZSmv1NBPItZnHl|`K>v%Hak9<{v5CFLzAgBY%y@bo{?{e)gX`&^VvN->Lim!1r%e{&Bs1e)t2v81)i|pK7z;^PKko zJLiXg*%R~F@xw<8wST6WR?~#~&9Cui>h{Hm@q8-5zhFOSe?MFOZ=TTpfA{?CuQPg) z9Dj(YKfl)XkI?pG{*r5l!VlPAH~RC_Kk^sL`EL>Uqu*8j!T4%mor#?aKi=`5+21~u zO861|lPmPc`sQHtg`}BQ$ocX)|-|n*fM#Rr5{r*vs z&bR=+U60OZr~G|(&S^P6Dk6V(>GB)MU)cSg%|9{n_qX+xcXRlazqR@QCf)wp@h9aa zB8@T)=odm>TYE1%zx`Kx{iE@{dzAfw_?c7^b$yAs1Acgiv%g5k59$T@$+cJEKZW^| z^!zT``&rA6f5fg2v&9yl&||!B^M8!^th?^tmpS{N)#VGgh)dT;Hv6f3h1!pC8OV#9 zR+r=35#uv`(ZAAxSCYYx&#e8P-oJ1qB0MABZ(Y*+v@Bl{_*4940@uic)Nwq1na`t- z|9t)L7&(6+BL2_l`wRHTHAJ_+AKUyBcIfY$Q40nEhZhjjvqHRH8{`Aa3;}&CpA^(+EnBT(~nQG7>$&Ml4 zbuug)rsou7OmKHd&!u0u^HYrQ^ESq~+}v#Aw!{jfva`nZ%P7nknwOtjm^*Izgzinp z%_?*kq;yWn%_~gF$(ZWyHJg4jc0BzkWpY7oj&&+8X==#R?oEUL&YhZ7=$@KaIIC%5 zg~79@X0X7Q1c9^ZHzC0CG!!`a?;tR_!b%?{t8=FmnSLookC~8_?H=zg$n4NRXZqBz zFNU}a3Nj|T+Za{~^W77&-I;|Q^tb)!lKiZ^!rXi@(#gk}x#Yr`g&q1TY56;efgA!}lap-$ZSg;^7_GBXM(Qmk(>v$H5h zu5!=nKU1PkbPp+@AIlu?cqs*nJuWnI;OnfZdD$KEkmqf!)6CrQ?)(8+?(Feb=1j;X zm&xBu&B!m9l#zXPUSU>lP62~_gAV2Jv`fdT;7!lUb7yDexUbGjF%Zr<;|WLycUn%t z3>kOof5Y6_?u-KW04g1A#0^f4*2%CO96au%lW+M}IVme|k~@F+bP6{5@)DF5^0Xr~ zmxsuYxH2)ZYI=Sag?HA??uq%i)AI(|%S)$}e0QEIFL`0VL@^2fHYFsa9j&&qiX!G_ ziW9di@Euj|4E``>yn8~g zj;X2eO6b3WE=jC9f>5(^CX$!w7gMjYd%NSMLG&46KLv33x1sL7G3b-L{EUfHGdc{- zy)I{pdzO3rU<#@HjO>CQhOQsjN~4F-XZi$0T@PbGcE-d4D@ePNSIS>kK(U!M{<@&a zv$H17&X`E`R2M5o@-i}~xW}tdnNB|*TaYzg)vWT2E=3Xo{|q|DYLL(_qCGDEQI{uZ z{o91ry1$D33!W+z!n2^fSV2K$ zbOqL73cHw2pcm;2z1NVx9h=^wBJIC$f!F4rWeL=OSW#9C@6P)t{pOZJ{LoZvyIJ)= z;UnmQe-cmD?o0luJ5u9gec(r0(fYSfl@+JjN&JL>|FN!m`qpy7SS-Y-`_%j~Kb&jGUNWP-?m0|Bw>EZbsL{>-B)-4#$`G4~D zv@Tc2p#S|-RsY<6y-c4Z_(h!sKj&wxti<6r=uY^T2Jy|D|Azm^Lr&s20_}m=KZ{!2;Z~1!CLM}cZlJ;+g{gLtMdztKC5{yst6^c(fx0kQg z&wI*q6Bhq-shcjJ=chm1MtHln7J>SR&Xeifj-N2*?dcr;htmEn z&iKAB+R*+XL44PtHo~BDyMMl}(b>cKXU=eP_KUQC&2VXdFn^cO_nrl`uKKzZ;lCzD zT#*)DzGNPS(wCjicBA-7w3pxHSE<~OC?~db3HQ&-GlejwlYc6DgAH z(AM$wSLA%n#s4?7eu&>6^rm+jU%n2~{k0_c1rqV|N6(=@asK(S0@ZJClojJs!s|C{ z-T(WkRmi_Ur2bt&lr$#rWwg%#ofpx$o!)vZvo~k|pd$L^_OgMW<2~KL7y4kWUN9bZ z+x>s2&B@AK`AwQa`M9I3=$RRAKkDbw8HOln-zX5Du9=2NrgJ+@bB!wD>>oCYEZG~h z-zebyFZD};{pu|ExvAE=Rvf-)PyT;Di0}E2_*cEqv>k{4{1CE#Ul8B_AMq=69sN9q zpT3R!AA)Zd^8PtQ{TE4&{we?5TJ#s4JgS2a`qG4%TdNH=71 zGj#+!yS(M2b?F>_`E!)NA^4vEh~KZr+!-8xo2nFdSwZ{pegy1>wA*iw&!t=Ma&z%{ zasuHWDJ$f@!lC^u^alm)iCpwdLvh;`;r(amh3q(e{7(zw8~QyDg%)e;(xPH?&uXC!E{yXBYjF$>G0F{`dSJ;4g3fNkIoWK2u z2*|GYsA<@v$b=ssH>_St0k03mu>F&<(b$R-*8qtS!tZYKu!K ziAH7GXYbqELeM$N0rcGZ%^fEBKVtmnx+av;pPliGj?un)%YyiM$7>50o!k90wsVV> zoPR3*OY!-OGk)sV4JeLVRLA2jUawz=cek-kn`P?eAX{UXu1G-V^fc_b~$Vi1oTX zm`4m;$c@X=Z9R!2Bp>Kk#9HK*q4Y z-qNY#Iseo;t#A{m^hyFg><7MhNTtWgKa;Ec!@eK7dZKFE?f8N9fgRS_MchCZ_q;^T zUyX>LPsc0#zzBLv(@KY@ohoO6b$4UL=eR{j*!LIKW~%rsBa$p!tbZ%M?roj>bvb~< zdb_Y2@}|R^zTxa||EDTn@DKcrcn_4@820yRGe)i-9^s$X6;-|>K7kuc!fwc8_5V4_ z;ZKzVRYb5K?;{4JwmWdH*UbYi8)$!F|EUN6&E)K#GG4_e?8kaA$Y0!#;T-k@7jo?5b>#c|5&lX4H9UXq_#XYf zdMy0f@2->c(<1N_1Mg=kJ@z#NmHhqHqyEPI&h3feSh2st1p7`1ii4 z>IL9?^}JO`|7R+0T!)nOXZ5i%9JrA0cK?=r|F`D^RlmiHkNH{Rrr;A=+jJK>cOmff zyQ9O??@r-QZLiL8=S~Pt^pLY&hUI3v3+PGf^}&a<`muG_h)1;f^0&cfwAN|JL)zfK zL!Q!(bxMFW5u}avq`j@B8_)0c^Je|D-IfCDv3__8Xo^U|_4K5AL_`WYrbZ-zo^2P5 zy_tTk9y7~Rc`cqZ2mc+C1ecy?liOjKHAldjYvazJkdf&&Zn;fP1j(C{HIx2%b1ps2 z?i)Ijr7&x%yTi5gkzxc6nJFe2PEPt?OnZPkr5|ZNNOTo(1X3uTH47r#Hc~OpDHUxe z6&B`okRP2g#*e32965b6va?mz2mf^~O^qnX%gtA_IcU}pO?t2t1!kv2(-9UzwkWYQ zh5cYiMjm~sW2?hdcm710zGF=Qil#$Nw+fk9L|JKhA?iFM72~1qi>AXe)0$-E&avhz zS;hyv(aC6QO@y-?6P6@fnyGA89Gp>jwKYHH2J4sBls3#s=@y(dlATdFRt1166m+gG zvu3CS=BI@GELb>5Np6mll1`~1N^*0;mEba)lHkvRN+;p!YcFDOib&FfZBIaX(+#71a0Apctbe0^?z z-xu|BAMavypJSiLGO;_2k5#8}0Dm=$(~HBO!`jpaPN5fa@VFP|ar&1uSN&V>0n@rx z&NB%eC%|(OU&ZP|GWOvqy`Z{~>_q5yL3Vj_RH__*_1MPOKiEn4_YD$gev2AEc0Hi_ zU&<=X3JA`BpATdp%Bcs)%~nJ{m_GPwfT?HJ=lEb z=R~jv>4v=LBXVR@})2OR^{|J-=z?IG4%ULmt^dNP(sI&od|x1?EG=>eVqSYlzy{O zf*6sY;?Mse?|(dEMf^QghvHT~|92qhg*@ol@g%2z0Hxm(qTlsUeERWx%Rzl$oC^Av z?$~<|r~d~T&msE#aq7o-bG-U@4w~=~r+;1W^ zZ_n9)bVF|by!$auzmN2rA^QDs>JRL@&MBww5ciOx`hneC{9o|60`T2wS_d+nq2;du zt-B`oJuCV00*j&7fA|6+*@=*D$deyEF^EflF~z?xH2q8X__O3Z{u+nm|HJX=zh*FvU!RBm=Wk#2D5rm~i^^Y!et(?$kuUN3|L^ouw{rT6?xyq` zA?bf4zVxS3LGad5_nw>$$aly$rpzkf^oz}u{t*5CIQ5UA{5SLBFMlmt{r)tk{}}n- z7oy*_G`{qIBd1){jX(XxYbH+O^gk}!|0Y5I`{UH#kNO{?F#hzvb?4u^IQ=Ur|9v6) zU600>{>x}Snm3C7QSU&G_-NywT>Z0tfbRc&W>SKLo+lifCy6*WCbtlhQU4TYwGfh> z2znvU^*38_`uFWp2(Hgf>p;y@4(cD;p3+J4a=fRTTa0$6n9e0T5%fa#{;5VPr@!hw z3c;+>kJS%-{->J?$rn{q^|9xbrb4n4K`&(4x#kN$V@y_85g=FNvnA4J;3llpK>Rrfb zzf5?9EB}9!eoylRDtEg7qxWxBRt4{Wq+OEG|9NL9)!Q@UuYV@beC+{F|3#$~Pc4J` z{c-AF(~{;@ET)?U2SVII9{AyKWzPS(164|W%lY_6{H3lZ{}UtrymTztiJ%v<(gdR) zr~ip(lz!u0(}J?+KlB@GTMEh0Uqr`}od|j%XC|9#IsK(wwf>6H%b#~L)qlj$Z)P8As_s$7hC^7xLEt&A^nIm^Qo3X@*nrA`p-wllAQ>8A%A-L-ix{PzjmF{@4C;l zpzQufKO^s@mO^r%-~Te{mh3>#3%SyGbOon>R<_bF?l&zYyMENad9RUvV%-1w=~%K8 zK`-R*%OK!vJ*isB8y%Qs=*^_XW25zgs!#pHe;VdcCE^uh7&Qd0s;2lAQ?c9^?xT z-dxJHKVl2{zE!aOajoY2ztS#AX#Xm#qCP`|`0qdWCp8+!>F-MU=?T&Ak5fPDL1k6s zC(;f1aQBi|ukSiR&0F8`k+KT2{{Rpoyr z9fyuTApSiwstC!bfAVHl5t5w<>4rRAZQqAn`d7{*gfb=@I5Gu^OYQ;cqe|B?3LqwvHHKT6Z&tPkm08E z)2N{$%wLrHJ!|Rxt9MQ7AJO#tJ`UHP_la})gI>tz$ISVKOaJFO|HZVPA^L;&Ur2xP z0jlqaU76JW7|E}lr|3o{b34Zmud@`XS2?IXsO^zx94MU2o|3uI>8I_dRcz0=9T|zJ9w=d?;=k$N7%b)o{^!zWT_mU+C^8eLx zB9+*Qpcit-lhfG#U*%sEg7+pWXhe4ZZ~Z??|B$P<9OKeo^rF)5`LZvoegvc(q#JTl*2FQK{sFCZO*{7gC;hN&^7aqTkp77J@0PFBFN_~e>%i{+ z!ttuVEn}{9eu6mT4W1X#xf3DXkRLs@wUkT$eS|N`boaNUUH@6~x9sox%p{9 z{mUrcW!@V!0YGvUdLH{<(!XNO!5y4_YkdmR@5+td|B~xeNJjlH*Qt=~MAS@M1bM^6 zv)6O|->XkkdU_;Ky|3p#Z07slGA<>d|B1L%-$s50@?HPyu|*qR;nF{y^h@&oY+CXU z(!_m3`<1;sPj++4YaRq+?r7|9<%Yde#3>`h)$SNf(8eH+z2| zUi~$H&Zx%ex7Hsa5RXoO)OAQ`EfV`WB=PG1{DO1&oc@-Xs{Feqq*+i(fAIbn^S{!c zND-1T@2ZrJB|DMb|6ey;%+9|`8L9Le6JzLqgY**z^wV)Eu@l+#*Ba1m510PJ_cg*x zD*o*0Z{3*2nP?rDlGjr#ZbVZ~tp32Z6yYOwBGePGV{Of5?{MQEQom;$KO_k30U#8Rc- zzD^OIH+d?z7LrF!qPn_6S&>|Y){r1}BDA}ZbB?}u0oVSXApK@PT3@1z8sK)ls^U;v zB_O3;k`PxL+tRv67t_tC13@oj!#kH9;PjW7P2-Ot`u%b0Z&I&}FjC{!U*_}Ln>qc< z)=+&CqTlseeCbb5qIzj5eK-)(4Vg5#B|HBt;|Kxt5BlF9r~XF`>a!e*Km7+zmcNio z{|B^wm51~{wp!=^>wNkjTOFSN7>9L8e#HF4m$$sc*8hzt|0Q|y)%b52D$=3l5Ak2* zuPP*C-jnx8RUz4lQ0^hS=2c+lKQ??s>36j?EhxMGL!GF+(Rh36H&rc``u$Y?Jl|Kf zI9~nkH>+hW_+ZRfS|HLb@UE`2D+|xcX<`9ZJ7wXIfBB z`h7oF6_TOf^=nlj*@>VR^2ev9_vG};_01$<-Lz=yoXNG$Lf1KyYn?d}${pmQY1^i7 z@&DFuRQ?Agh^Z-h{Qpfp{-^4u2KC=7)Lfb?;xGTBzgU;U=|8%bVt;T@zdug>GiH+A zYn;nJ{0@1fx9>^L|K}+El6+lFOUiVHmcMqDsJ)~2Ka4XMSloqNZr++GlIdKs6G1Oz z(N({{#p(Zr(l5zetNI^uow-o`-RNBQ_lieVuo%yO&CwNvWGBLZAWxNRS%%YZt$!y$ z@>RY&=}$K*2+7dzqGQQUg#SR^_EW{bIsH#%DFm~JX&os2!SR4 zs&hY`OLii}J!Jl;OKWrSclHB{ziY_9#XA1q;^V)#SZPB1Tl;Z87JvLFR=DRXPXDwU zsQ)o6K{V*0^!pw3H|P9oBl(--&S+_tL0Jq7oy+wHlKdG{##d5T+(|uPK2C+ z+eKv>AE^B0H?lZh{YS3M?#k&O zyFlspH&^A)NxzHqONRd9*^Pu`CqlX*cO0JU=JdBaqYyl4rgfn72k-wMr20s{XXVLh zZ1IM{)K>gN>&6r`wm4q>-#+;M?VSG3OI7-fq)`7e`lpkAV(2fP(^yD$BBUGg^@9u9 z`tM!JPf5N_{VeH}ezRd?TBkXvKlz%*Lb4N~+(Wk9YL4dO|8uGT`UF~^QrpJp0Dt`+J;&fwt zzx3UWg=8m!UdVHIEiA{Sf5m#0exq#k_%E7I=_iK%)D?||WG8}N$OexLPvZ37G+yg( z72W^7^^JvO=ogzww`2!`UdXK@GoIq~FGKwoLx1V$#zJyH|76lF*@2)JGV$j>=W_ZV z(&-odObf{=|I@#q{3eF}_pHDdHs@A-*ft{^^*z2 zr~AK7{x{#E`iD56pN_r6P6WM>-KVr=??3ix|9b|Q7L=3zV$vlU`cvPb`iIzwpcnGN zu@#PR=|A;?u79ow)gK&xNBiTQ*GNdl^Pl9~ssBanM9>Sl_>xXHar))@(vq-lwNv`d z!{mQrq`!!cB|8!HLSFYs-z}VeYyD{g!MfH?`je^uB^mnj=vcB7K`-Q)GLNq2^vm^y zC1KrbxehgV|5x-;IUyPMUtT(v>_pHD+2_m8KH~KMG*=;bK2ZH0xsG(G{wz}A*_$X_ ztr}Py=zp}Pb7Ch#KMDFz|N6@F)c=Z@e`)FefaYK7eWj!6_inEj?EjGNqPOZfsTa@- zxu|buGME12l~nwhPxQ91?D`S^$$c9L$$0;(h>j&Y5%fa#?KA6lPJiZch2Tw8<9|wj zu>U3JKgsd-yu)=Zmh+$dv`!@EL&U3p`HV-{@xM|Xe_prhf7|u{O<#K#(fwD?dKPb7 zOy~8_U;n$!OP}DEYm(HL%qR3h?)F_-mD8U`>G6f=_s6Nj1Ecv$ML2AeCL0U^UeO4%l|r8>-zsz6@PM_c?$W!`U!7z3D56Q z^HjPK15W8jxr4mnstwn%`cEhX@1!*Az-gU&-;pLla;$agVGr~|7Hk-GJ*Qu;k1q-9 z?mOw1>*P!RullcbAAgn8Up!j*-`8=p!vw5?ayLre_TCi{I6Cci>p;u=f!j`*@+PMke@aDs6H3}pHTVt zjY$wo_Uq?A2l)73vS0ZL<-cMzs)x<8PU<7tpXCkCzRvmoZ=HT$zVg4^ryw-`<@`U_ zzF>VT=l}he`A-QQzyFKV|IVK(37$>L|4-i^?*CWzQs1l@z5h)~l|1xPb#BnHWG6zt zL;gH^96SHxYzO66akiHQW!Ha{oZ-KN#`~!LmE299*QEB4WXQ7O5oSOyVR(%81A6X*YQ-Tq?V3b~I&X#8XRUr6(a z(f>=PW64egy^u$KefR*Uzt2Mo!B?Q-&#wO&S^lX1kxc)Vd>K9RlKqcVI+pB2&S_@uf<~IsG+nR6%4ushrtjUwHn%*+Iphr!DCx?zf2i->$4ErDMrX zgnAEh@aVzT{u=i6zvj#KFEhBmhU*Ky{@X%&C87R%h~mI3=iL86x*=PIsei5mf#R>UZ<(z)#h3xd!cg;BchL3D_2le~o)c@#2vir{X_0Q>Crvs-yqY6EL3eoTS zGQRYiWVdfa{Q5i3Yj-E7f7r$J{3%4gKTiD@enWmM@2nqj2RZZSR;Re~x9=IkA#$}) z@waPVc>B9n_BW2w`*zRL^MB&4qtto&Mw)*>>_pHDxwQH&cKq*&TXp%Lsmj079~^(l zD<*xz-s7DAQ@0bs#00TxUpEWNrPnJ~Rt4XG zlXgi$`A0uceH-}+y^#J#Uj3BQ-;4HtF+=x%anPUKOzD9BtSiVb{q;>*r22^dSEYGt zKjiYi+$ZW{SB7aFT7; zx2i$+nbnd+>63LVmg6x}e-WKab|S`9qT>kI7L*e?nsm`R-6B_@}=|4&OjVqmR08#ECd$*{Cbze{2!8j@tyL& zcdIS^6-vbUq#yo)+`WF_G%o)?OeX)&O%Pw6(E0y8pZ{N;P;rXb(qHr)rI#4`OTMoyBs&rGLN4Dq(Z}hR`vXdH>G*TfFZT(Q4E=JS zKx?l+2LqrN^4Sp&v+I9!(fU27qQ{?~_6?K_{Y59pAH+^%kH3$mJynj2KTH29djGDO zs{b9+Pr6H%hwC@!+=-BG$ez#6W1l~N_^m?lltxd#>l@lPkr?Suro52sME3MgZ~lj@ z|02qtrN1d$j<+;_KzeGUyS&Z`$$Sg`sF^7lAQ>8A#Wdj z5j*~NRQq5265api4^;mVLw_k9OLii=|F>2hJBIVW)bFAFGqI1HQ~XKY#xvpiji;UU zBi)dLUUb#w%74XwRSJx`(bF&Y*_4d*%Y8N_I}!9kHkx&LA5MRZPZff93Sa-urhofr zo%f=zDF2Bc%cpwSFTE{5n^pDl`ulSWuzt#UYlZ2miNyhV^;v-ZZ zh@Hru|5?Qsu=9_ke$Q#={14SHMuzK8r*kJlx*;dm>n`V?NAy1*uR-sT4yq8i|1oma zIE;U@%8x+*Gqt#k?0;haQuM3h)!*r^h7&pezkZ2oe~ckYpTrNn|0>C$eqI{&v)=7& z@nAaib)vOs-tKI1y!uP`blS-2FW#XLJhz$Ffs_8cS2_#H&|ma&XCc{%;2+5Pes5#) zf5iPy@(N1N{R!k(-T(bv#i8;_aQqv7M_d*A=(-EyAAiaCC&k)d-TwTob$`;dD5&2b zr~Vr{(*0+Y{dbUV$g>@5xVZFRb0PKrL-f1O#Fzd*XkL~#!@2(ry^tmKTw6H(6UI{c z3(@b7Q~#0Swof}VJ~v}zxs?K=z*aBU4O)vepxQgC;tS0=k*ojc@Kz{e|0hh+X|Kn$pq4HVFaL7>fww9>Z!KzLac}wjiOyXQ z(fTLEP6WM>@BX%78CU+-A6NR#C93~t*N^!QB@ea{l6O|2xUZHdOwujciJ%uU`_nJ# zarz&sto6?`EhxKwEB&O;zlhRLY^9&hUE~kRP6WM>qq07(#p&NzP3iZ=O8*0teqt;A zG!e!m-IARMdLi$4>i(}d{l~O^f3)<=i8l*L7qOLoI``b&#^QMOuP;53#_7MbhW7u% z(euBQbW669ZOkT#RFHv0?g@JE~dS=%EHb5EO2 z``1}}{^hs}JIEar>EsflE&fmYCgv2#Z60j|ze2X`*X$oIeZOv1Es1NoYF{NxX#0xw zq)T3)^=*lr0h? zR;AxRmrpw6Q!A1)uQbxTb7Yi$mRttuyEO-wPYlg#WacTV9zcXoE}kOKMT z%r2?jI)rQz8oC{5$Q87!Y5$orI3~KS>#SWLBMK7y3R$Duuxq*fZM%J^{0jeKpWj## z`wYkH-+AjVEzjxS^|yKklPFX^BOg8As`6wk>}#ojJ)XU)eLG+MH!kRXnA1P9Ej0*4 zNI&$uzE|U^u*37N*1PAw>EE|38xk;pN-DPjjquEAyBS#69Gu-nGhb`oGDHp&$F3 zL+bs_VHcy?`U-j>2i3Z!DyP5U4=Udg|HxlRq1ThZ{XOLQ=Kn2y*JpG3PmNdUchc`V zuF3)QLY}YwyXO78mDAtJ9bLcg|44tVJFx!gMMBL;6Pbv-}au|LRA<&+$ELl)IoznR==Ed7yV_y7kZd-c$81 zewkcOwR`v$^0l*SevS7w-TvSL2Z?$Ia_2)|H{kkrWveRxdk3p@wxs#g@*CXuzWZVL z`zw#o{2IKw67BsZ=!KlOZgd?^e|N3lHRS(Sf8XwvYH<26jE^Lk0R#JK;A!p?+_|~5#v|8 z=2Q&3Jh`H>L$wOBUQ%>zCmLrgP7-FFqDr{d2}v|DfIX%~SP^SNHpjL1~sM{6ALx z1HF)!XP)?l(|_ee)&EBQ2YgSN{+2$^eX8H-sDGl<{j0hI?Q7lTSa+{eGxA2O?dr#j z9;5%8n>!`TP5C&|#RHHmRpA-p3AcPaWsZmrzZO$pyJN-J7Z( zhwLfun4+wydD$IuCrl`C%RBClM!tJOwmY-1!*J^+-P-WoO3Jw0+-xfi(KdvqTksmZ40xex;tD;AKF(Lg=BwbhdxU2B}Vw=q-v^@D(jcpNxA76SrV|L z(N=#m-kq7NGNhBj)Zb_6Uv>`uGNUk~pZaw)b#3J+#V~h4ZuZTV?H!HG+?+!9Ol#;u zN|iqOtMNGnG@L>A%a$uu;LxE?ocu+;yI^{DVN`WlIV!8HoS2c7@9r>kx(onIjrC=R zAsP7vlQOce&Xe&`U@41{Z(NzLx;tr8HKu(Zxs!gqJ*1wf8mwnSaaI({vHIY;VHig! z?wTx$8=)2rUV(2=zAN7TQ6CzgtvgcpFEBr;=iM}B(xIG?`_juk9U|@-Kd^Tb)z=~WQ+u?2*~gItDfLS-tWO~@u7iBgUk3#ALcX~6 zNLg-te9|tuq;YV6VCy9``U-j>?fS7l_L8w=6EjW@y^vQ{J-Lk2UrOm0Mo_;;z62MC zex%#!JqhTA-2YlB`+m+WO2648s9#i%Pe1%0ul^OU_w;e;zlZdDLiBs$)c=hf7NPy2 z&!Tve?U#rES zM>zclr%(s3MNq#dPW{_fQvar5S&`e5>Ir6_Jm`g7d)>svoc`m_(EPxbLH**w_|pFi z?Q1Nb)6cnw`kVCrrk$V{^30_#%;fa{`yO2xvOk|EPW@SOYVOKJQ8|;@eF{-|5&6AsSy7*)=XTXc)&H^t+C6-?XZMjV)hbjdXWt*J?5$M( zt}G|yy6~ax-`({5)wPS-yCuy5Y9CkAy7;40M6WdR8~wkC&P(XrPImpi zgT}X<{>=9YCyIXWCR&$$a!`Nj<`m(fbLcOoa~GZ4$*#Z5Er)OC^xrs<(m%*K{i2xC zcXv>K5uN+!9Qu>VJ};fy3Hc8B@b%-^{h@0}|Cm{Ug*cctF2=Pv1lH69WApZ5IU64SB~4dscAz zA8kwN3R$0A)aBEU_1Terv^!WY9(tYRZ}%>)&Go-lT}AUF4hQQGkJfL;k@`dXUsxYK zy;5sojBjQCd7vIa`u%^_`;kk3+u!KY0W_kZ*Jl>>`1H$lt0fuMrw~}T8|zi;uLA;l zA$!&OFoDzGvxxHX>IAVC`R|}#uG=fgus&&>20E8wth>$ct-^InL=HypApj zS)W(b=hKh%c@3=r{V((b1B&$l^g_13ZT?zL|0lFQujks}`k)>M{c_zfNrv?)gf%R^ zDoOl6jfUsP*zMo%PyM-*)Bi#*iocNcNks!b{dWCWH+I8dY6pLct^eU2@4wFJ-!Ov8 z|H$C_KpqGESRV=LA4=`Nw^Ho#k9xS{ug^S8`XlbYZgo@o-Q{R~rtteOycc8kYamMd z)E8I}sf5m5ed`OXPh|FQAkyjFPRMu2-gmq|ic5cYS|7=z^^p!r;qFXnvGjH~e@dlJz3}uYVR0C!X)Oj;nuaH7p~wEYi+!s7@o4?H?}C);R!B0ePa&{w1=g$3 zUk3#ALOxb!5PSbs{~XmLljwc0X3GEKBHsVW%@nkuHO#n-`XdX$l^{_oGAGKa@}J;f5l0YeW0bBhHWP zo!^Ype|lpX;Y|qY7X}~yPWp|n8j7MfBlW}YkUdYgJj3PxWq(or1Txu}5RZ(cciKQbY#|38<8wPpS8yR1F!i;yBF zwWE5D?lXN|>HPPUK))}&TYF*Bxt;9#xBm}s-vJ**)xCWQN$9fl4nq$}2~wrV&_O9d zEJ%?J5Q>yU2nqrThd z3I6i73IBGUAGbU{elXZx98_A5-OD9DJTP!On93Q=Ul! zrGvG~M&~Q2%TWPb$hNKTf8K(xBIR$-l+niV z$@e@-`A=SwD1Th(-_opq`Qx$rW1dL7dAg`~K|Yp$=Q>mWR<&Pc3`m;Tx5Jrn-n!cyX^aUY4|&;MesaeSTAUFMyqsz1hkB#Qq}g&k{5`d_5R zH=Q!R8uyVX{`&_beNFfeTqomxijd(Dx=oIsh|be~p9%tUR_j4A67eHZeCLlf9ZmR; z+$ldSfsgm1f7L19+r>pcYj8+^_uRSEc4a(#}Q??Pb_{JB%cUlA;;Gnn{>-`4XH#$Q z%;__7I}DkZy&#A5pl;Hb>r*mkbGPpqJZPJK?`kSLZLKuwhs>HcEptj14drep;o4Bg z<)v{ExjQ^}9zD=O_Mo4~#}bs)RRg`LSGGv^DfP+5bPp@jx+=PmamFM2Mjpo-pTMD zSeEz>D;j%c?iBYw%0hjX$ctUcSO^FY_h6pBnW5=c{Uh{lAo*26K}|;A`ra zCclNwt!v8)`0PpA8E z$d@5^omGX~$Li_x=c-&z`R=R8g-MqV(hjKKLozRRX;O$URC}iNzT~YgM+I;p->tE+ zoe95ki2B)l_|E?kKI{&;=AObIP2tbu6WCd&nZw^dPJzcN_{S;mScPc&JdIP}u?lc{ z_}|hl`6TO)`xqZ_;VugQj;UguklzPRN3;FcsRv>Iy6xrf!|P=I0q=!`-jFkvd}^G( z%J}STh08c)sjh->Lp!k8N98zq%&#ffPkSknuXt4U(*-}NDA>aR7xLX<84eC#)+UGKIc$H!5(}ClD1Q1AG{1^G@Etm4kN9UlN&LveA>4ug^>f>}eYl=JK5Ar; z+J|=~i!nV_`tNK`|LDF%_>V|ZzdvqH|9bwjeOASfP5IBnBx)b%k{ZRgx+I4Gk-`t% zZN`V*kZaHXy}K#=8J}HP>8;y8TK+C+Asq53f77pQA=sn*&8K_p;b4ErxnEU}kp4x! zPwKX$^w-QAA7XVir$62PJU+&ueKisPUd^R;N?KPP4!F?2MeWac{jMV6{~4#hbzb|3 zqwu9wy!rDwe)eU>hd<&8;z5)ACj=mOt;V* z5_&_nzpPSCQ~38hCZ7n^wA}+4{$4)}HGxI*7S3bdqWYFY7m?q5RW9~E@L_+*)1JMo zz!d&7Nq@VAFZ{bF7XFBr$PW+?fDgSP-+bry`)GW`nHV2}^1s^KRKEDeM>z_=r0_4J z_FD_GKl zYPUvsfCTvC{F;~QRpRzRy?s3Ae9He?CE;hn>5*9c3qLL6UnEZc2fZP;Hr{`UN&k=U zmQ2I7Y?p)7-^>5e4`Xkq{GB}dNyBu^9{r^0FH`wI9uByWe~oIe$%KED!k=BRw(ZJz z_>q)Wf<5p>Dq)caej(js4+mVxCe@Y~n((W)mikA&C>}qU)m*R#eu$3Q13!n3*~1~- zK~8u*cHv$USJ(e>%3nec(m2$|tGM3HZ|OYrL>1S&`7WJXPgZfg ze48jz|LwD~z*zsAU0cTg(3`gV=sh$J$z=cVPD=0OH`5n<=*F_5fR5P*r%>J5Dl3AY z(=mBC*d6k~gSRX)hW~n%{_0A69*1ed&#PHout)j})T%DnBR+)an0-9AXZl|+oBC(U zITXjgEhmnhF2mD0%N+j4h!;DAztcedK3k>_dDr_7y>aE}1?_nKklHB!F#m9`vVUZ} zZ2xAQvh)!04W0&%vc(#eB7XCo2S%odJ{`UL{5#<3vhl+`Y_9(V8%f^4A2N)}pNCUC z{re`Se@ M`eDXzIUbk5#hn|yhX9+pa5K)-*v;DTCBew{)`{)n;Nyhqjs z*##}zmAd^MEq=drL#(|Y*6zD6G5lLr*#3udWd8y!{{NOj<3si)N9}L*PfUNre>+$H zUWbI@I*F?%@RLf&=L49A3j_$T=U(vcqiJoJXc7w$RwRKPJy-p|v24*an=P_pll`e(EK zL(j+N|63MP{+sXRzt*6{!v9cT`5tj-fQBSbp*LjO!sGv%^sjxjw7*lMzx$wW|2OFS z)@nlghd!U`dVG(B>74yI$`82So3WMdlZOK?&hJ{%cPHszP0xSF)FJ&3r3%dR6_3jH z9LY*hyo{ZXe=sZ7-ZxT{x5@q}zadB3eSYQim!al|*sA^E%^Qhf9|~0EoWW-MV}3@O z`rXbd(*Ed2$McZTAM%XvkKSssKhIBLXQ_EH|5x)=fD3u^kn27&;XiVP!WS|gNqjH; zeM$BSU7bvO^|o<+urYC^_K&>R=$!mmI_L1ud!5e7!vPoa!+k>rQur5Xe|V@3<(psd z{2ck=V+~Er{z&L!RvMLOGNE)~{6lf12#+KkWZTf8atM>UPi={yaa3ou%d_6*o_a=L-4f z3GrMZ%#-1HLhRvy3whb$@ei2dAI}eB=ahiY^Mu#~pXUj&2R_deVh;yg$Vy|{7}s~> z`9bWg+S2av^yhg(?19hogxCY0=LxZg11{vohI@?dKTVYVBXwo|EAhSluXAsr`1uy4 zx3dY4Jmc}apa>na$NV7Vf${L4oblu>Ci@5J{p=#$WcoMh?+{*S2hGbO5B;rhJHfsZ zeey-(wWo$H%Zms^DF zQ%L9R;p(ab;Llt=`VC6|Mf$%PKYR|ATRqJB`|%5}iN()Z(N6gB16O1s_oOkcZRlfAG-}3rzNBe0v1hKaT!>{QO)E ze|{`}=rRun!4cGJh+#>0p@x!A^)}OHpD82jfZORY)_z}7f2fZOHopr=m{!Xo{^dDWk{&@wI z{>TGg>?SPozz@?gdpKSHgB4zTz@$Iphci-zwcKC+c=bPoKV#=-#Nua+*6`^*9Q1~~ zcGxXlOyR!>`k!08{-He-{^X&5Asw@a{`nsgKk{(8{v$d~TxQar@x!A@|CsW(sQ!!{ z7#oY9Pv?Goi_YPoH{@j-hW0V(zj~v5!X8t+{y87D6YQaXgpSz*KYbt7hseX}`X5@7 z$MaK)#DB&QpG*3;vE4`Y@*m;P*!jye{7@`@nC`LpPcvR88}%E^OU60IKjAzc{>XXVTAT2%D=*WZC3?G${;%*Kp3z~N3IB7d|FaAJ zkMXyp|GvS*2p0@TA6aJ^F{vY`tsZcCE(}ebQA0m|3!8;8k62FI{q(A_t?Xs-2<}k`V+?WSqd0`fVVz?bD_+K%BmMZ za=KubCWZKs>Ve$v4n9bS>UNR#zh7#99W;f1;05_aBhGfChifLr;G_jYy7m(Mn z%sDUi9QeBax4vBO36uUSDgH;kN)_ujD*Z1q>%V@ZcgQBo3yk|mjhiC%iu`T6kMcSU{_%%c zhauxD+Ao|u)?=`~K8^NoJ5BT;LvuK!A3RO4hlAe0fBxSqdH=p5^oRD|g8a&YeYC%KOJxxv zkN*E0I%W@-t`5LAtwQhufBXxck>UO#OzF@0s*;$h*2l0WnDw8ksup-(eHH)2(E)&r zk@D^IU8Sz(tQ~)*A3xkx{^EMFe8Il9+?=zu27q=V;-NaTyqofpHkH0@5Ai#VZC@GR6NDGvyOO ztG|1|>!A4K|D5wFf1>uLP?PemuiHSL=Ql1d*q0#yxBa_Od67pR4o8rVu=)GDp508^ z4du_fOX>Lu6{732Sd+}*kM&v76hKGxzmGG=Kj1=sAH3JNf9=i>>b)HYh?0+M(S+qV{#^~~{SM-XM{~vB#W9)zZ9{A;@9=iQMNuu(J z#?oKW8Nuz&hQ;zu4%FaOSK`^Fryf06H>@ek1x-_lF_+U}#g4vjzj zQO;y635bHBf#~;Fn9d_~j&dh|DXk$n46)tYXBdpWc%#n?Cj285|D6Qz?Zp9+GsMHs zr}Ge<13%}A0L@qS@PP|ir~jApO!2>c#WJqmwk`EveZZ`La0|_^Adm7khmP3;KmDnS z)E*yy{=-)-t4%cZe~c=i^naeWK94oo9RIOCPMXVBh}D&3I*RVgpsqu}5|DSD{C+g2 zKYjjD2VP&}hg8vfihQ4wAU?*whi6N^z=Z@Zm z#fblPQ2$;+1Pl&(L;n4BWQ8gH^ZHEe9JMY~aqBekT1^=L$Lln)M}3gjX<`otT*x=` z3ersY@6&qw?CkmS`&;6B{aKf{#&?6{En*Mb#keJYjyrSUSl({H2rHLg+= z-n>ybE7&fdutwYNfsT*wpLANI zj6LFigpS!G{uj_OdpO`i?&#Beh$;Rb-y@$0mw@l|BW(7g$R5=0Vi6X5;0NfKJsfZ$ zm!I2pl_~sJQ2Yx%=L`Rt|C{*t&+^3^O!!Mf6#u7q_|_%=8~nrTk2W#c|JY_}F)MX| zdk`=Dt$}23^02?4{D(d4Ur6`Z!@=&5Z(n^=Q(zR&0$dpOt~=QTo2rc_N%F4F(L{Tq7z z=hW!>jMiLp{Kxu?(En<*|Mav05hG!D$ik;q9;Waw(*EGQOzF?JKBS}YVRuL!A9jH~ zUn6R?V`fo^<^)%@Jc)2s(XPh|gf53+v{PF?r z|19$TQ@E1aAAR^vg7}+M`?o&t2#P>&$hk>1EK~futFqK9@`TKPw(f{+|KCE>GPu0U zY1z#6GwD;^p>^S{R?S?G`glH_vxfsNWa^AxjPu|2(f1#_NP9oNm;Py}qi`D2_?MGS zUEiLfxZB?IFP)RGIftG#)IVW&$a9V-Pczv+pTmD|3TO-KQgiq}S15myrhZwWvJ8Kr z)^m!!KpjAD$eZ5!@^+K`-(q}nR`~(0WSp|X>wiG~JJ_L_VBdrEW&I0IZ|3?NI?(tT z3ZH`ZbWR=)xR76NzcpmSZ_DeWl8c;YyYij;V)Gy5bHPbf1bgIn5jtj%d@mzNbJ57d z0T*)GhOhUS@P}L@@xu#b|5H4CYgc8#9{4sLvj=`U9kYi6E@b+QL)}c}|4Ax;LicetH_X2Zlm3F%$6ee;;yZEh7t=UpcEHE{lgMQ<-nch?|15QkdPqk5s*O4Q-`Psq zEu`8%e&_OcYEtw`Yk@iZpSel?1o&04p7%IA`mF!JQ74e=_J?0*eEi1gp5Y!ob328< zRbD)EljP+jh>!N?(D~-?U*vm`>xTW(%9Q^F=28F4U*7mT>$1f3FKPWB#DB=1tveg{ z?{5C5v`ARt&S(;w|DgWs45jhMN&h1CPsX<| zqxlbi$aGvBexPM6zD4JL{Cqlx11{vO)+3Dj4~|_cpKzX+Yp)P=2!9h-=Q~TxxbEgHkE(&tMa{(DYkoHQ+tfR{*QLv$n5HZ zy}K^=4^;n}Tix}$yHnXs=Y@2SJsfZ${~j`Ot_i=|-|`82vD7;re!->H1$*Gz^Q%*v zVRZrg2;E~3m#z*hC96GrsH5Nhtj0# zAKyKYSRWhuBVHgp5DwshaEDy?>65!n;eSoKw6N1t=0A*MihouYif`nx9(Ot&vq!%} zpa;!ICl3c)NUPt&87BPo3P0GZc>IEn6#vKrKSIasfuDB<&5tJ!7s7#(?QgndFxS7F zd=<~s{awT#J7{c31CtNVc16a7DiV^V=?;ZqolL*uQl@_Z5s| zvVYDC4Fr3HcgA+={~(WW579mLa25`fY*O%HTdx0En*LwEK>2T01yS!enV(r#n)R>u zn~W!E>V24hYF&|-{^ieURn4UTSCs#RnGybqUMrKan%Yti@MV9Nih z)^vSq3i43ZWKl@>*~1~-LH2oM+smB(_5R1>88rVlCkc-UYnfU9KR$@nzohSPuSO+b zJz!DbOSZqG?T?k$kH$YQqmOFkoH+PXKaRz}T-Cor*C%Fwaq|Tqo9xf{>}-Y0IR5WX ztW%FRz@`;q+xr>~jXhUtMAvV?d1{s3ud@CCgD=Y=!%Or9bjL$PSlmxygiI@S3zxutPuhV92Af@gMbHQG@E1+L%!T)49|ZmQvN=QN7v`q*NKPTkd@D9@U=;Q-XDRT)lA~Xvp?^Xz#jVZJ_+ohKkt*k9u9g# zZry*8as8!sxupLUKK)lDroX#C!l<%hI2p*Y%ZeJ6q>P9E!?@M=nC!ou^bh&=XK+ph zzaPyLX-&BO%hGzZLIg>|!S0Yp@7-q{fAh&n`9x%f?H&YaKXQNhi}o+%fBWcH_AOq2 zH^yJ$zI>b4ehK`B+lCm&pVqfX|L;;n@R%%rot5VNk9o06QvfB6KSj7hPOG+=zkiCv z|4IER|0VlxJ*mq7Ys}%l^<*sm$UCUsOzn{F_a{ljLq$*M-(UB?)SxeyzqpU@uMhI+ zy~~osJE`*f+fn!u%^UyyPO2=Uq5q82Wc|zf$lU&>2t zHQWEWFJtvDY5cj4-)fv4^Zfz+TU<-{H&+s)GF14-!5>BS6n4=6NG++qc*?9l;vM8& zqh2%S|2saGA>!Em-2=w)$A6muc^{rg#oDg_m725} ztm7}T{q_3S>Qxl~s(S73&MLG0u|Ep*FRA?r@dtcfxnciNQ~PJ1A1EHL@b*Wst~29f ze-z+nT`TRM==y^nUC_ul|D@x;lA(Rm0QZ1#{PnL2WYWlm)c-Od-SvBEKNjRQELZQB z^{?RH9Rz#0baeo}UyQ!w2e$vJwvu7I`*+cIP&eeqg~ zN&nxK{?^(3+&A++JpS~*f$F2&zZCA%)%9<7rt`7%J$6=C*XOJI1$53H4!DqKwm$1h z6MnfdD*V;HBRYQI6^WnGm)bkYpFdHa7tk?#=Hn2bkloew@$g6eQGXKIKS~h!Z`uEl z{isnD?Z4tX*`MG%E$uP%=ur2S$$@;q)|LAddFA_eR4n<^V?6xs&wpwh|H%04!Z*lp zm->6}|Aiuu|NS~%y!DS@cgQJSJ6~sNf9+28w`!!2UaI`N$(;U~7rQj~zYrz0zXBKX z&>uU?o5KG{b@^WB9BFSo{1MOd7IYQt5#O!Lx(fD)_xW_p9uDaa^6XFQJY*_=rnCN{ zf~Wt@iRr&f<)iWTKY-qlsU0ufWzxSU&9`G`FR@*z>yP?p{y@@?e1lao{-+P>D%hj` z>CiEIIN(CQ=j?7|!hdLi#E-<_d*eSRP(6?9_c_;eaXspLg)6!U_TN(dh35x{=stTm z;6jd{x3ZZD|HzdR-@2%u`>3uz>Y3I63UBhj&!c1Z$bSQ58}@J(4wSrX>T^$V`(Kgv zFG1fw(<_QDt5p2I#T@=!sGhmG3cr;d8S5MgQk< z!Vk{y@SRh{kHoG5pu4&dE;SRa_Kd+87g@5(S zq+P|^vi?)%UoL;V__vvQ?ELxPMV+XeqkW=q?G|eC@Sj6%;6h%rwqvF#|GWCriXwb# zl32S=h5v14{nxIG4S$S(3f-Dm{riSM%9$qoTEABiA^Ls-zH^HB_HBvb7p(p1ArpR& zij{*HK^k+h1eVuqx z>`?fdV|gImL5}=2*|D{(mv-~Aa@x(!oRigOp8D;iY3j%AE}5S_ zH+mVbXlC){p3RE>JbMmp?lmVTcTuylfl)cRGqdMr&c;(kfV+CY+3L3%FfQX20LFFi z=N_=QTyE4ie&Pb!<*r9Nxj|Y^w!7tBJGs}`VS&)l;<;qqY)ql*W;e*Z_)PM2JoHGVy z=4Q^)ZQZpUQJ1#Hk0(v$Ur(A!KbO|Vjp$pTHE#Hy@agTwk9hx3?}hC2*(zCVZT zq;HRUN#btIe_AK=KYU3--kmFvP_Mx_6wC*@`BSQwQa_zIA|-Gkn>D??r3t@oknkJQ z`WLD_!HI)UQ{-F+{En1tA}gq+WCVTxtu8B`pngJ@Sg%3;+WTMr@a8+Y{U=@Pf65+8 z<@!Bd`;&FI)Zcqj9`U{aNxpgs#^t1MqJ0@=w1|$&ap=5|&UIIH=83;d_~)&r`qu^z z-$@W3-v#L#Thac!%|+u(YDc4bL_VG8)4A??JuvX33IC(>N&ovjd~5xw>2Ezmcs~8} z=saxD|Dicge`v!0>@%vr`S6_t@z11oN7gs^6756Pw3XOK?PPpjK<9aMj(7*z@aH){ zn&RKG&neGslOztGF6-siJ?8k&>+-Wh{5yxTWovU;!SkV=Dzr~H#ZQaAgLYOSeds#U zjeJUoE`!V6O5^V<>ixg{BE&nw>#vrfXS{kZ`d#zawh;wAy>(YI?rbA+==?g0|3PYZ zMLjrQt0zNm$UjGhzclH8+Iz(CEN}f)>t3_|SbrD#V_n)C?kBxzkaosaX@9Io3tY&} zzf$$} z2Rm(~@cS}V@VZ-E2LWF)iJPBU54023ByT@*ht6#}|ANE4EwxiVNb9~$Y$vXv?-FhY z&v}T}0i*kP4`jQCKOSMyf6dLL{*WXwNcCqp_nGa_>w>ULmqHvM{1D}TZRk3;&sAkU zG!NG0Me$JAzg4@729f@ib^Qxppyv;#()te~Mt|flfwL>oy5E(=KEmaGuMnN*(>car z2ZvV@q4JfY4>ioKV#3efLyC<}66w#V^uNK3pZ<(AJn)hKqZ~}94j-pZ zio2A7sO#mYD!9ZMuU@;+l>dEGiSpk9@Ar~KN>!Hbe==! zush`RK@IOR>3@>ve_TxKe|)a=zu&C?9LguzLI3ZoQu?EGU$>={zfk<+^CL>iUwi-G zozY;sN&lJ?sQmlXr@zAYo|H!|-!BuRKhk-qEv-vMejw!|k<(K|Aswfahu)C4Kk(7t zCjHyqN%7A&{?6KH)*s{VpnvEBnci7%o8sYp1LXO}O2l_XN)#XIaQ!R&8NaSR|LDHa z9FJ24_xtzVx$5l>CjE0?r1X;O>;HeitUty-K>x9AD83Uv9tR}`|o zG~s{2`up&mQ^1cXe7j}I_?>1>FsA>)CKUht<6j<1On~}j+i4QtAz70BtIV*C^`FeU2!El6Z*59Ue~$lFTNVB!OTs@q?1CL8 z{loJq{C)UNg7|EI=X9yRXjd}+LPy$rR^8}^@gIYQ~59QHp5JS_F6*Sii9 z>&@uxOjoLY086c(1U#w^xDMxsRt1_fzPc=cfBX@NkL!7H@XIS4eZJQ<>N@6Opu9;} zFNEw`^@dWBr>If5qYl9xa{z&g-^oPhEc%45CB(-8p`I^#6RP>t9Oz&^Xyn zsN=)_&8tqD;KzsktAC;C|5)kt5AKPzKX4)YUfXw+3BR|7pI1tJ@nP}!M`xe?f(gIo ze>D9cFP;8Q3Ha?ETinrv-|Nv>{Jc}dNBR%9mF?yTf5dy(zv02FEXEJlmGyjt|B-hk z{+7Gt^}rKm{hw3qbNYTeFRJTkj|ZMEx7mAcGwC0u{yx9Ui-R98|AGF&{rzInU%GlJ zcod#hqlyW?;YYFffhW!SmxOPn#Nq=Na^Bq^j4g|6=h!%sFoZUrK!EN69;${lER;zSB+kxtje0PnS;r@UO+=-_xYhYbN};wBCl_<((ov z^1qVO|LQhbPnht}Evtq9GpD9M@JmX6=hoYNmkEExFPi?P#J3+WKK>{5y{x(ke~X46 zc(#=KmyrHWu08p)3I94h{+AYio9ur_{I^u3LjLpMwAbsI@Nao4*8YL#N~b^it$`0* z$p1{8{Im(b+0Iz}yi(#v>dSdxz(@TD@?g#D*Zc9q82_x5zk%mVr@!--?OGi8X`217 z>H5M(KR)VTd3yb~wD>1u@hyc9S@G-B9Y6lUGcw#?+@|F}fvu&}-)dca{p0?XSIqO{ zL;rdwwD2z_e&kR;S0C6P@eZ=bZ&w~N;eW2D|81qy--(#GZe$EgpZu`s%4B z{PM5H;^&R$(S{a-De{*i9Qmw!_>*4t#l zuc_9xfSvMA5g+A`_&YZLf!>f;eDH#?{WqtK_Wk=>>GZeP$HqV4Le3xdZwr(D>-GHq z6!D?Iv)*=1i1064mByoH4VjlYcfst;c{6ht&Gm++UPc$@=CC>&p{rY46-Axr@?VtC$+5Rv7DeJ$^ zQd!SKeCd`VujBg@*JD$a5Vv-}@kW~KQg5g>{_|H2Kk&w>;rCSgkVpQM;SYT1jq}IW z&2Pc=Z+-k#ffoPrP7!}5_U%&jf8ZhBK|WvOTlf3RZmsO4zJK>m=MGfQ1>QV0{V!GH zCc}IBdoQ6Qgg@l&E3WG04}W}rizF@lmO0}hyGvFx(;^vR55#*441q(_@gSRaO-8q*ctm!03Z75@|@?& zcK6#K`Tqf3|6Qfi-`QJy{`c&%`zD$2ha_v^UrKyyUo8GAWhKa)|1;t~KYov%8SXD` z{9X(Hw@RnK9Vov1xpwQ(zy0{IzoqrR=ba)x@_*}-;_=@Z*(S?`zg5@&?b7KV`LKBW z+(wHBneZnckF|eZDe+6vf8xBCjNf0~wr@%X<)8BeNw*Ld^9x`+6-*5Q!NBUp! zwWfb5@k8@%SC4r4&+o}wcA4<+{5&@O1>P;4{=t2*{a?@<^5tophnes{`c%`ul=zs3 z4gJNkw)1o6EtryPU11HjcxF&e?z~>s*mSGyjNIIu4*c(o!SiSm6kqLRwauD0FPjg~ zu-Z<}&Yn$2o$04JnNwzU7?|Zw7otmDthW5B&S%hZSO1$vWaVZK&B~-nMf2U4T1$%| zkUf7+&g5wwrbb^&@NLz_{ zGIC~CG$3;3Wlo=y**B zP#1eygJ)+>pYH}@4{K=~29N<8|9xX-F3z&ZRK#LMnRqe5)wy^3DKj$XTJuTI*@QcD z`i$HT=Vr}cFq`IiMeUI}cS_dm!DON~uCd}jFJAcvKDIG=;g!}TgZbcn7 z$e5R%n;kU-$IjwzQJ6QjLQ==7WXm(*AhlF)s}BR4_lx>Jbp^<2mqxJTmQrZ%zCL zm680csr}qn#p72{jrI>D4}KXn0)l-!e)-)4f_*#(UXXu{9&iJVZLzS7{3HS+yUhGb{FcXBB0rY|FwC)dc%^4$L9<^lAPi@hh_a zQiSLKz3%a|)Hp8if|UHqMAtvqq~@#W;~_CGA^odrBLBysC;-}wh< zob4MPzi_eqy!9DMvR|NAHNiff124!`BRbx1;y0c6g?#*iY8)8s26@WiJBq@Wd^`tU zklXSPTw&r@C5gT--t_Dz)HqG>g4Fq)>?wmHUi>Q>DSi0(ISKO1Q{&jI{Wf0dN~8}?Ba!a$`N3|GO=kS`t|@5y_q)^kc%F>>wYOhhj1qJLUXYuQPnl!lmwr?} z87V9C=XG@IpGOkSpFf~I9?4^#QceNwA4;CLKIQpJ5xU2o*K^}}yrJC{T`%8W^gnn( z9!a~gv1xsSS2+FdN)}^OdUmX-a&^znDb!^W}f(^<(+WULAWb z-(K`T(k*1QDid=|>Ep=dWrVfci+_<%6N`T()n76weH1*|swgpa1-u|Dckeja#IFzS z8y52M3mr&|U%dQfAeTqEwC>;I#BE87xcm^VkPGWxx4@KsyHm48==VzEUX>oLgJyml z4LGKzDa2)}s^;vG?H(Y~lIDf-Jb1)MT-POdL3Zl*>S9y;yRf!OA9Zc_75C8hh`)TB zs_+WlrSKw;^pQcw?0NmD(2bOS=$Jj$ON-Dk`*;q#Alr^_u;0Y5)=%;c)=t|!;B^B0 z{IE_yVJ+I9gFMy?$f;dju*bRq5jtj%^#d~MR2S^yIq-sf_KRSpIT)rWAmCRq_ z@r%?aeDdHIX+Y=Xqx>4uIeG9CjforkI1aoZ2Q<0S_*ph%MpP8{HBhv=W*y2ssHtTY>09F&M|eQ{lbG}y4Cp|%!S!ze9BhqeiXM32d~9}^#*wz4)!Ij!y(^Z z^gnn(R<5#poQdBRWJYuAbF)r!{Nc!1`?lMgng{Ff>&^`7G zY|ox-$D+*S6_jg`tClV_?qB~Whi_kR{f^+5l4pu~F(lRx>7lNFO7YBUN$HlB9=4Y! zixm1iv1tDOkzSG??5uuXM8Iy4Uyko^gUNopz6ZOoT34jlbv|fq58(1TAMC$KrTU&) z=Y!Y!@UQd1YkkCX;03w$vHO2C@%s<2ze3I_qrNW*&tHDH^IR!CICHzX{>SMuzbmA3 z_GeQ14K1K`M`m|(J@|*|9{YF>ydaxBQ^mMG>&h?W8|;f@xUSzATmE7lri?qgQF`bW zU60A8WA<2=DV>hlN57BOl0M|)ImA22NALJ0Y^qrLt7P_iha+RGweOP(^-cb)#Fb{wG>A^E7-c$WV>?17l zT)&HSNQvfgJ!;7OF0}hJsz;tCo;okF^owwX45Ti3)Womsmy%z&gXA~W<>#$~vxVZ9 z(~0tvA1b*1Hj1BI-de{hxSrQzb?AF3P*}nBTtDF7izAe<*~f9<1-bRi8SPB`czqgn zVYMz!aqHCZS~dQ4YIv<0|2j3iRt@$G<#lS<$8+EXc}d$JUNZ3;|B!rx9hC7dKcB|doy|L-x8){_SnBAe<`&$lb=ZTMfp}-{tRAGnY6f17TzN+o6HtH(A-?ESAbbhmkJ#PFyclS4@mqgCC z-B%rS`!ButmqYorxIbBh-%oY@qpExh(mDI?RK7)cT{k*r&-tPK7`2bnG5dH<=a`B@F9y!nvUJR{Hic>Tfjk5XNq zD8D^xPakD!AKG~q^&b@`N7qNQk4l~@)d)ylAI)FCitY~(knsXMZe;%1$s*Xasq1lF zm&lhPFS=;Ne@yki&xxP-iE}*ZA_?+arPhIo*M6UWW>XO$AJ6IG`_RuL&N7AX1GmaI z*pJEd%j>xL>-R_>g|ucHd!&z`U0<+A`pD~BU$95|h|q7@$8)e7kIbroF2ZdGyXB|U$f?fe1nsFmU|E{d~*iWr~P}pb^07ScKs+> zpU$Im_6Y9~9kY+;bbcKMwJ^5dXOER{uxiS5DEWEyD{iml_LaN|gin6rhjjiM)d%R9 z{Vb|h@OjR~#Gm|)RHf&#u3!?`k9<6bcn6uZqv-%s{Oj45>Lb4;i;RV;zxii#{L5Gv z8~;vI?bV^%q}{;-`AcMV9TB{q8h_(B-F_Jp-@MBd|F(Z4Ul5)nd3{Xb<*(o0QIo=t z`b+HrHC_J=eNqJLQTagU>@%o6CSuY4Idsf^IrSg0{lq|ekbN8nyFng3w87ZFaCQgk zza;y?MSd~cZ!697^{3x>`{CL-jIaC*6p`Wpr}bG^o2_ja)kC@M*3LTn!<08*B`_AA@ARe`;S(p`tvnj z`xf}}!bf5+iJEJfsx zq2H5_=fDf{pI491HSv39g?vM>hsB-+WfcQ{;=g>X& zSf4kaj@id^;02jK`$yyW5_kW2dP#VRv>)%^?yn!feudA`_bz$ZFMVqR!5;P#&r@BQ z{GPp3f2c+Eg{SFznS4Blcn3LQ@wlKVf2q2X-jI_dj$bL`llWWmM7{#a`>wM?{!(oj zl^;&BcyJS?Uy^%np>(?;S-e8&+3A`h&ZF4$QvrGKiw3*ORLHhU{#@j zBFFo0`}61bsXZBa2g`+&!u;pFA35iD;oQ2e&uA#`i_0koVIRkV7v#S1yLC+bDm0P& z!dbTarpi=b^@neDy6xw)DovgjK3i9?&v}jbQGFoiB|0Xbr|w%X)D`UGIq-sf_5QmC znfNvOUcMnTTaLrHy&%?pO((?idzB)LpYIcth4}l=@1kT#y-xR@ zOctSX9b7Nw(T|swr8GhN>5}hUfx@$N#**B zzCL}O0hG`C_Gh+}Y?r4@HD3LN_h^?;QQ2)>Qoc48S(kSZjiHcGwYecH@&lD;tY`W7vA64 z|9v^@Y~oDiy=8TDeOWS_Km7PJ*nj@jAbIZ4@7c$5;01ZK%4^2{{gV{F?1H(yT`Bo_ z?c*3fm%gu~V9)8)qIKW%_jYtW(yK$~?2)hJ({I_wbKr&h!MyB!v_DQYef$vb4{cvh zZSmzLKW9%b_YtDuI_B%&uh_p|bj}wY-Tj)eUv!Ypk$!lu=n$R9b2`6aYi@hl#EClgoANG^Zp>y!#y`=N#Je~tT$WWs}2Tk!WXMqaefBU(w;{8?q z_4|Ukl%I{F_NygbT_0{s;Y88dU$|^8z|%AI~BELT0X< z_g7_VO3L=+9qvsrLJL?SGf zHQDd?TO|``oV1^*e@0wN?Hj@JuIKg_s|NMYWR`b5x3>i8+@^E(+&>e!lI%ynXCKeu z4&*HtRQk)r@67{}UvPYUenH|F+D_v~$TL5O_(Wbl&Gl~o7Mz$ltENGBik+0}ZkjldCYa8fnUkw#OuM@z zW>1?oKa1xkceXHjb&NYv*L}jBhKlRF6C+*dDz|DJkUjUZta&sOm}bdFot|>5y|?*c zF8>Q_t9r%A4EMJy?iM0c3#9>tV@2D&jw_HAC|uat?tdW;oGITEIU)0Fby;*iC-yqx z5AH+i{JPG+vOD#k*6X3IFOz#6jsLF$7R0rZ?Y;t6Aa#E2K9ud{Sl0=-b5bNZ)@||>+!yD!7oh_ zsxx)1TmMW``TBPqW%xEKL-wl+7F178kl(nCl7~~KKU8+*#7ormoM=1Th;?1uNRaxgF`bX4yK6*UE z@9Z>Z8UqVda_q|%4FE^{Za@Fc?3 zQkVYO(76`d&r$DPsM<%5Eu;8H_Jea0X&u#!KQTSE0-RE%yQn%k-Dxbo-6u>V~_6w3pr9}644FzoXN!Sfi=NB)1=<^z3A4MlB^ziNd(J!6+ z;TuxU! zonHC~$Kkh5X)f7Q*v07|Z>O%8l)tFgMa8ZkCj4XCzb~?Y;!QeY8F8%+2GK{|1Lo)d zPMb*a&dx~|*1?J*xTj)tTzEd6=h56_%yY9pt0?m6yd<~w_l_T%_`Pr)@jJugCu-Qn zSMj_?KR@j6nEnLqC%f9Wzb~EV&^gwTu%D!Pk94j(#2Z|{uJ!3xneyik50D*v^Lrh| z56?p~KX$hJUkJ>Pj`n-0>zAtdhkU!F{2B3Z*EQRAo6^T)ls@ccNzwV+c1_6uydZUc zE3|xA(GylZnLbL&|H|V5C2NMSc!j>di>#m5^BD2#mKx<3Ns!-AwQirI%2~iUiXX0f zDG$Gp66qH5;?<4b_1h2qe>{IS@`=YURLgc>0J}lz_WM(%({R0kF~HT-bltG4ZX$~)B42io!M?UWvxQW_XVX`jAB!gL;_bBtq3 zf0^c7(z)&s@4$a()k`m8`+Zkm#t+o5j=oLdOPf7o{eZCI2fIP)_Cq{ zMr)bh&<66oD1W7)C4s{i%z>}S^l z1K-1R$n$FbD=Rq!qmQ zl3XqOBj$(uj@m!uop-3N`H{yj*ib%+aD~*vx3dagtecJZ);LSXztE#H+>w9aJ|yf0 zS^JYKTlw2xQ2w?j``Mkm_$L}A#_u1Lv**N?zpcthe)@X*;03AIyVCCJxV#*FpNCZV zV*T%b$bO+mQljDOB*?F(;%Dc2?ShQmPhHpRh2RCLhx_Qw+mEyT^!^v|EIo0CXFt2K z?Y;ux3aN*0;8_)pbIt;!D^V{DYUw8CM|6U4#r3BO&)(|~U)V2$_=SA@A_?+qtHM#| z(LuYe+X;MhelO2z@Pmn;^)5Zp*|T4$iS51uc7xRIcNfM3{w32XaF?k38N3tackSQ> zi%k4J9!B*)>cm6&VOfdsYp%XC?2F~N6YwjGd|QQYN&H~P2O4e}>F0;^8=~;F`=v(t zg%jlWD(VMaW9@ewOU$Y3CGmq^+m2;D?&pX2*LyPAud8RjVAI6xm!<6I?2WZwM^(O+ z6ux@Cc;%-D8ZkdRU#2_oYeVZp*xfvSqFG}6233&nu~dA89nQh^Q*56c|J1Kfo4$Wn zrIG!*d;FXP`Ssr*`Jo;Uei4=L;JTg<<9CR6IN#d;#41zx273~}9v(ltx$QoJbPK7c z-_!d_eh5e4%vAOZPmbF<%msK30oklOEQkpko|p%#hR@B29R`vuqc6>q3>*0r%ssxJ6(pm^OUS#B^v+IPPDt5;$KBQ{&l^A^5@&i(f;XW zM2hg%EyK8ev8Ii1x~E0QH8^x`(fR%7#`Z(LOP@gge#*es*mLlQgxw&2XnFE)fBZxF zvGhWUe`k5?lZe)~`wFC6NUTpH?+P)av9wbtJ+}ULRwtFNzmWB=c=iJ?$ZA{1yk-jD zCht-G`F6IS`uBt!VpNxUEdr@%Z5>$j-mD{J_ueM3)Trg^%1%;{ooj z5anm5$tPepNZo$TQ4jqE2=42|C$_Yot@KQnmy7&Ix`90B!Havi{F=z;!>awhXI(1a z4tTj`Btd?9J2(jF^BUvX&r%P7-z6*Wb@P{y;ulicXY_RVyfB3HIDu40f5_)7?ikPD6ZB|>` zeFu0!PEk!D@~#kLRQLuP%Y4Lri2h+-USz=l_fkCj!EWF`a6_GC%r9a|y^($!-a&TR zo)TT(B^-wzudBk&cK-`epxzTa6kETFmT$^_arr5N<&}JQ>W?w$H=~cV-}@_w-^(7q z;OUY9>;|dxn{cy=$DhU8Z~b}lI@-sh8>uM0AS`tN`;}?2d5u4OVZXIsQ2tW4V$^=3 zo$WpeUXVJ!PO6@4)s^9>-~{%2#C|*R><3s~R@LXjPru(`B z4&<3ne0DL}uSoy&HN;O0Ng{qKUU#tFSD{4}lKHc<-TyQ+T=B0+={iU}vgwD+*Bx(P zscdwe;$;*75xTCsh3djsCEK5~(;Z*3u&Lznof?0@>mP-`NflGo`cYO#+kJvw8zXsL zBzCs@UkLQW=;IIi){yUyWXgI2;+J|XDsUa~^Wu5AXK?&mrTC%$V?n(#!uraK4dDd& zrK2B9i+>;B`)!KM7ZH9@V(P$B68EcqIjXBae?k2;s{+-ph#%}1Ot;-9OR`_IeNy|* z_&}wblJXawU**X^|A5lwV2w z=2eQ?&ptywQBwH!o~iQ739;>yLsY#Xw57j`oT2b_F;}}VuYQA7Rg;qHg`b!2Mfq`X z3We`EU_*I99DclR7(3hjFGRjdXA!M^Z4<_C9G2y>_h$8=5+NlK{vE9S?)Z)GwDkKm zuTS?)vY4atmr!TheG>15)cIA7Q+{Nt@-3wF3@9GD054o`v$I7jZhz6+cQZer{BJl| zP+8K&b|1kNNS)vC^JO@Mu8*z196LAmx|ef-iIn=v2iZrG&gb@x;4`t|yZ1K=vk_oH zTu+gowZhB~?HlpzhwHGT{oAJc{`8CdS-eO#JM3v0?3$SUnjoF3ei&G%3;MTIyaYb( zL+aN<9Y$xe{q*piN8uat*-vy!jNdv{j$s@Ga0^uY#&u6~{6fk!b&7KCk5@*1;m@D( z{nqLtN*^P=@O2X8*8}57HujGJZm*Re2*;Qo(TUFc<_2{-asHyWZ!9PK**<=DciVjx z7(?pm1L3IWFJCBrxQ^#_ewMlb{v-dXzSM6&l)o=$Q~u}U7fFy`OOzkV-oVGW%96@o z@WONbE?RzxKYbv7Uc>o|k6);V?LGp%A$9xB$NVuZe_p6v*V6&`==?5!yQOh__Ft60 zh;RxX6V{owdkkKXI=`igM@0Mn=mfi}>+$$m3K8ieb@ADsa`>K5=?mW zY8-y!weoEi@)526rUD4<5bs5`X$Y{+vPiv&cw_@)Nxh(DjZ6(AME(i!Tihp=@<3W zUpW5x>}U73T_}Vrq#nMgKL@q+doKF7N+{nfr4aZ{dhw@6{NaoIZ^-ZTeR#JQ{~`(U z`?tTUXK4IJRFv247kjx3;)O4GLH@okD`K+WPA)$trAEUy)W>$;3%fz;_N#$@%JuU1 z?nCsiyLLSuKTACTd1BnMd)R(Mn@Roh)cB3QRQ`s1;cJ~`yHA1_q|WbV)edeik>8OD zPFU*wmeLdU!F|Y(I3Wk=#P~rQXtQP_w z_aSw=Uiw;Aa#B_FpgMDA1^BVfj6E9f5>!o zU@3Xv(C8KZ^o#zt_7%x~nP>%~Hl8^ARv(Zrveo;xC?270GM%c+w7#FTE3Tt_#eGOi z9YDUEvu22i-%FG}>?6sd_Y@Ux`q}Q2&>NE1Wo2i(|7pn;)jx=Ol)5ZtAl!eH>82!p zh=*U@e`Q%eKa_8$AE)x;^^_>TNP_(E9jWuXtw6;~Rc|PXAJX^lc{9%U^F#jJcoXqE z-&;R9)L&W%c7xRISGT6*VNHw8Urx1s2==>X?~~pA{9wOMUsL|#<7W*>j9)dyPq*LQ z^;Q3jYS)TqKRtbnPOf{JpC8i4J;X1x(+l5lg8brzZ^KzC9&eKACQ*L3UjD!jCVsb0 zrSjt~k6&k@5|7T#O$|rgv>{RJ7UW>ef)hq`{6qB z=Zw8C9%6o0a}_^S|MVbUU-xLT(uV2^Llfio-_9=sFUSj?TeyYz73n|fL+gx%F7V2a zu;Par1EgMlVBNKx-ITx6`e3KJPNJnO0@?0{w_3CP(v&H7kriz2PD&3r8yAQx_ki0GkJKOzFTVbnyj>!Dj_Jca= zy8hj+9*YW{-^%)HK4X4)8o$#vQ<}Z6T$Eqr|B9d1E)ylD4s?E7EAH;b{2Yy6a65gU zJ08E#aNB(Y><0ONZ9l|2$kZb}j&S^g9%$bP?Vo$?!Y@w{mk}DYaw}{{d5MUkKhsCTSnRLli&r(>!P!>-T#8d8OZnO zXI-!3P9p|MG#Pg#Yh_@P{b)cK8UPx%s!gTS~A zs}bb`yw2$?(wD}4Z0k?w4S2?$bRFz^bt;w-(ppN ziH@UFcq<={P1guNbvr7MZXqXp*=)E!|3mx6%E~l8r%rkC@hJJ;;27I|2f`JS_YGiY zyZ>p(U>W&)w9~80qL)fHw%TvNOL_Q(l$JUL|K)Gja>xILweStn`uk#HlGwUKeou(= zZ1+j=H*xBxXAu#_^4_|%!B9{sR1%9~n*>CZBPhY5s59hE%>?>ZM=%eB1Ce zeIir&W}5#SoK!^=?yMZ0{~Mw6z^=;C`M(*#Dnih??qEM$uf4U`du+djEtQ?r`d1?k zQ~7am65-415ycPT3d#J~+3tTKCLLD4uOC}~M!nT}R(_WteyPWz0@o2ge|YhmDafEQ$_b!_}AY5ZDfrfe6C7yrNugzxT@lWgk0>~=fZF9;S? z)?H}k_sEU%P4cc+KhWnV*G*C3Q^Ne@9*P*`4?ks%_ve3G)%g2!iC?5wCG~4cD+%)3 ztMXxSG`9SG3)j`WRz02p2)rP>ZYqqK{|kQ34zgb#k6&=Cv=A%`soU?r^ky7vtr{Zf$OOoa3K9 zzJ$Mz?86nJ^S7M;BYq`)U+U?jZ>#q@arvf?AG(<82hPvsqx|e~(n1JV$Wygnumt;A z%9eGN40b%)fYV2)l}ul#-`{u<*>4J1P+Ez@@0pwAi){72lhsr@RrM5gStM7Fy&f-r zLApWtGAjGm9d7=o_~ohZx7UeZ*vBt4UNQhPlrf5nv# z^7ltQ{0!f}A5i)TPfZff+$8n3CfM#zzzb66cVjpC`{184AI5w0)cRGyowD6hk7xKD z!WH+g`1sjr%x|-@AIgtyRK8h%d8sL!AisF)PyYws=c;}H`+ygutqu^*H~;(OZ+`ow zsqo!H^@HGpk?B^(Z1NWch%C0JWE!@x~!X5n1zP`^vfBhc(I;2zk zDecb#XHT--LtKG;kH`6vv)%tfz<&Dp7mN#p{eZ6?iwf|A+*2;M7ul~!`@y|y>5&=U z{y`DN4>eXu>>p$+1hkLGYZw2v2Ic$pnGk=M4m zD4^f#4t9h5^NW@nO!oVY(ucStDY`$8m1(=Lg54l_e;#(W`(KDpDB!~)Rh(!>*JwYK z^b6_yKFX&^Pm6emUn1$yeg@ngv%>5DQIALG>EQgmHD71?;~(0eQ}@y@BHr_1#gE`! z2bn`S>}>ZxVP?ms19e#pLcSA}ztqDKzk}Uyf7!7*&HVh3K5|~6`k#+qaI$H*}B_Pc3=IRD$Hb`txVl&wiEcwT-x5_>jDyht2=dp5~8+X~=A zw6|*Gn?69fqOK~%GEKZI|;lk~&fBr)VsiRUE9Zx+^nR^!p}Kfn)i?8lex zXMT4o`yu^)Lj1xjXn&7mGJNfswhINjK?aY-hVOrye$!nPub-y<;q7k!mbxr5RQu39 z9}_>C1pC=x$uAOz-)Dzo`L$8`i@0$BUg=8oPlvA>=$<2f#r%j)kUlJRqn1N|OYYFe)kn$^fpC8Q4WZ1P3vIiY%gnU>HoG zq7GgIis&ecV89ij&PQ=d@pbSG{FU zF+G1c_~Jf%a^5Jp*y_I!-A8CX@v|<6Iaa0Gp2HXR+=$QWI_-!0>Bv6he^-NocAo6bC+888 zi{2{4=FXhHLv=k*ZKQv>T))D6MEHGdhqig8E9)UW=ae}4^7#An3C-zu3)9c%(Q@)l z}>AKu6I?Cw~aBWQolDrCiN}p5J_p8_=r(001j^9D|kGE?eTG&%;wGN9y zoAxVpm>AMyqCYg@l^rEj2%PWiemD1TfF3QFs8^7*CjU~adx z`u0+Me(7tet?ByTinSk|D(x3;%k>Lx|JzIcCl*u|z5d|(-!xkt31Vo|eyh0tXMUe- z)(dl)!&GMc&%KtmkbDW|!}C$NFWK+9%3{GT%_pXN^T~O^wG`&8}7cD2>rt_J@Xg_Vw^KL{=KS}+RX(#+X z;-TAry!s+%`v%&__tX6?(Y!{=es+o}KYLNyehKD7yA`y__xbsB+z=YKmvksmMf^e93RG3?ZqYNUh^z~! z+@kIEsOXnrbIIoj=&sS5Pm}(a+fJuTpmR{|@tmOg%NvKK>=!AvRXpqlZStWWYL2h6 z5a0Lkb;7?GC#5W2gZ}Hursqh$Jmy3Hx+`eht;C?56rVN2R!4#h+T`29`OKW(#P|M7SH`{g{vbZ< ztTbO_*8j$Sw~si_)&Cj!cbP2z9DLSg{~Nwgbd2l!GVnc0e9@t4`^Eo9e4jU+J>DyQ z>+~c4J3Gx6n*G1AUzPj+cD0|ve*aMZwa!WNiOc^xe8N~>IIhtuul{xQXIbpS)V%gfx_viM zPQE^m>Ux@4f0@JetHk);iU9UuoRrE*t?89l_I>pp*{`zs{`aVr#P@Kv*mJ7;{cmHc zU&{2tn7^%zaBi`C`_YpqJ?Z@~_Q(5Qgbf@qfFruH{;GENThe|Z9)B^&&u`ZI)kWl{ z%3|3H{d{CyVXGsMuF%VP9$UTD|NTd5e2dex{Y*PCPOAMhpCgW$*;$Q`8Q}KGVa%8R z%V9LWa+)t*L44Nf2j>ej7xcR;pYQD~|Hd(2Y(KRf+>+)CMYM%rH|RsQ-?oGE!EVs+ zywd4snSV|D1wSK#-?LKbE9O-UU$nu&`3A84;D0X;KWv}0-#EknZmLOqf28@M6~q@h z^5A?(SLhM97skBO_w(As_h*{VzOrKM7hiF3zFf8+bWusQtDSs!K3~7FGK~*XJ=K3B z;lpS4-}tbPF8>k*?)Kee3mtFoJv6?^-*aE};!6@=;*2z3WWKlk{P;p$Du!=K<)hX3 z)NED(`{h4J>3de1&${Z+_@WiW*W;9?TfOYJ{I$x$8k**dA1YtJa@ucErM4H%m@+nh z#Q8JEPbu!&-aYH}Iin}dDCD?H$4@WpIC#e7F@@8eF>+Gt_IgI^8B->Yo?bL=^rRu- z;_*|b6t%JP!_%kIY}thuO)4B)+>y_wrp-?8JEJ%}qqx0QIDPum>1}d2p`aTt?%CRF z{_-_+pOlL|@noe}QkX`gPW3OseKOfOS4bI6$&Gua)L+x*2hMQqf$nf4BdmUe{(%en z@cZBT*^95u?fQuL4LU!`d2(fK1^Id{@yPpnvBd{SN5Bv@cK=;xjjS9sa)g#Mbq3h4I{avnkFJPTbLlKrTk zUha%mN|4@h3QKuswA(%j4vSw~2r1uE7`Bz#k8j6oTi)~Hdv>DcOH|Y8>&I8JlIBq* zN%7rId?bE+!8>T)NRsmMy*_;DCtiG4UZ?p&IS1#nZ*3){fG@m^=Cvg8;}gqU2`S~} zoBZ>pVP1S!AEEigO-{aa{y3H#M1JRqG`gy?PA~i2V}z9P^j(?U9-4KGlJ(>d&i!k2 z)ZxT?#|SA{;CIk>wY&HYFTNGb7u{*ALv{M5`NUXVPq$YP56K4NbJo+ZAU?25Ek7%A zhMzaWetb7?tkl?x@BOaqe=hq`e&7?))K-ee-qS)zSyYYQcTJ`5slRNYWEYixG909B zQof_>P1H%-`{|E0Z}jUHLJAhx4Z6~k=VwvShe)pxp^t?B-s<79z64v;m#lGvw{`4Fkr*JNXvA}N7!`6N#pC1pf--fe^ z%7S_zoYxi*}9hhE^i^43&Z0j**~nY`;I>H;a18`QZ=XyXq+-iKgu*ZY&?4 zU;2VC_$1Bqd)YB5z9fZ%6vl#dg?_5){D-{qZ^`5Oh-j_eYUNJ)@0`crL&~p_tEve5 zzFJB!FMvg1DVPr+m)51A{4S++DWB5uQS!e4txLfIF6g;m+}Fg5Z);K?5iRVcR^&Xi zPQD9kYyY;l(*1)3_KEML`y0ucBZ+?zmCv+I36+*SNKL9I1Z9yX_b^CXR>J!+9XTGUmtT9%JwpCI+2my)tyE$UZK z@(P(OLdQpI*Hz-j7wS^b%ZpD;);YmC>)?F2Cx8!O@F5I7gu#cfAK%dWo%(z6bvl)E z>d=GpC6A_;yzGLYAwV8NG_LKkH^Q+Pr25ppN(Ic(Zj&l5~^xGtR zduX2A*J(Qm{5A4M8|nw*&zE)n^OhIiv~2Bvp<&+X3%=lYWFHdnmD0Kt@Rfc~VG{7! zKenNM`)R%ytxLgz@(X(Ov|9Ii)vvbBr}`hwpN1v0%vP%?sh}UHdV{lmZ_YD^<53@t z)XW!&8u?<_II?d?^5x7Et|9_wX4IX{_PA{}g{4>nJk zoU6<^Pnn#n%sEe)oU07wiJYfQ3hMWAo-!#|zyX4(`6Kt_93?ZylYi6wB|_VzV8I&n{-X!p z;>9P&x0g%gL|bXim%jftKg@mq{JOIahp5k(l%F`9pgvksej&cZt(0G>&zqEB6Zi4z zN9Z58pfCMr`A{#utu+3*TteBslrAqH?)TtB7<>qqm+#u@m+bT6dz|bq7ptVVkm-fw3@)zuR3fYt7+N#=KVgOl&Br!qdJ~`fL?3|`ba6hpr zECma=ptI+<{LYI{j&G`#Q*EU*AKm|TERD@d_NqzskwHzB{6zW3{wGVshcs1k7nxmd zPttZN_tc;R>3itlVB#g&vrBKcX+-~+bt&@xA&(Exx)dzng0>b6pX9~Yl=(1@su!RA z80C-BYDy$uocL@COObpbYA;RFZ%dJUp@x)+XqyztCy&pibtzcD1%32sgL1t1r$|Q3wp)R zoeuZn3o~D|wl4p?_$*4F;Pz@tRQk@LOiE!Xk}qLV`V*@ZWj{*qIIT-rN89D`rL-;u z3%Hw{-nkj$`WdKeQ(&J2$8IgUwUz$$^6ALJ3twqDN+lP?zRP zzLE%eSv042b0xC;k?9}mO4~`WfD5`PvU#qT{eGeGo#hh!R@+aG6YAuX`NLj7_KRCe zK9Tu_?x*n=EG4r1wy1qA{Gz3#8}BbRSwc#FLpt}3^t|_yr9}RgTu^>N-+IL6g;c*f zpnrYN+qCm}>G22cdu(~QKuri5<2MdaTZMpKuO38vc~yjLcaC?<5(6$Fdo8Xac5czS z!$(@5(4NYO?&;?U*bRE!@QX{l?03^r{e$rd`u-s~vP8Okez+r@_dc@U)J96I#$>-{ z6fUALiF$uP+e^ZYl&Jd!g_F}7DS3vH$N7BEmv=z@VALH?WO?y@_kcda;&JBWICSzV zVR!2o_xbaUaPWD(J@P8eOG^@9BZ%>V8sqf!&}_KkJD+FFuri zp=R3uA{x&cDdpWnd>vyO>WB(nk+}rh!9a#Sx6uS8XFFrNC3ms&I zG+%B@cm8#a^Fm|2ILCRBW4#O{f5kX2a;z8BXXH39Qot+6d69wz{sVo~>@Mp2BJ=&s zD?xfb|1=AS39(vpvQ-7N9RI`le2(!~{ocGR+J84*JM- zzi;repB2^zgyev|w46yCPX9ywFZo#$AqC_0*t?ntDX>?Rwn;(#uO!h#NJ0HCK-;8X z0T=WMZLd4Ri*NjHK4P1#R^&Jp&i)5iPU+7ILj zDOkV-{YvPEkQd*i+w>6_M@x=lP?7m#E;$IUMQZTD@1VcjcHgsJ_m3l2QUBqYG`?CD zG88S52W+Kr-++F+N@W#4K>epr3a3%NZeLXlYeV*JQB_=gD&0RQd^5+(`<`e?;|r0N z_3Mof(|)H?X5s~0&|4RkdX9f|Bz@l|W~ceA2W@o*a6y}V?_NXKLf?z6+(Y^I)+*vL zN(blnW1*Lb=PbI$_gc7c$B1$Kk3Tm6{6UVMW_arzF_>8ttD^_S8q>Z~G~} zA;hz=K*?=X1+h9;6-f$9nbwZdkHV4b3Y5tEon_PYTuVWjI3XQm}vvdd2eX zQ7=B(zg{lZTwCe?RzG``)X*Pay!o-p)c-(1&_8_N8y%A_ljOGWBeqlmvCui(U&9<1 z^cs$jkLsz8gFPNZd?Q`1qMTDZ(LZoO-)Z+4=Ee8)xs*2@OYWn#3V{o{EWX4?+J8+x z(YLQUUs-(PK6~RvFTVCk%7Bg~mLXsCUd>_h#ZS<7EsJmXwd3EcL`d8B9GlB1tzvGj=_@V}1?&F#9fy4AaaGQJxL%&de?II^1?jH$9 zp9*(>L%`W_+Qqz0()ti<*3vqNaKs>t8?nWt5W- zBHThsS#2uXpxY zZrDj%Azi*8{_>`iUzU6U9{&vK8++RAf5B(G?YH5NZtB$0IoclhooW0%b9{SmL@C|9 zw%#A}EPw+$S-b-M^!6{FkbGU45Ba0jB1hXF9g{pnzG2NYhspQee0RLbhjc~!iXq*C z&h&--cB+OPz8F#^8*CMTS3;MSKkWA2?>*UMn9#mUi{l#5A=RRkvcyK|N#TTD) zk~$9Qi05InOAI3Q_`K-~pJVH7$R;qr5 z`01y1b=eR8XAOge^pU~mZMA|}=rZlM)I0y$vk&fnmi}4Mn{I9%x9j{#7*2xF|`{pY*U*Ltz_`qTM zU;IX$zVI_n&Q!e*@eSWRU)|rZ9p!Vu_inz@3g&}d!GYf&z$?)CvF^6henBo@@{IO_ z@Qb!O68QqU%>2>TJAYV{jr-dQ{*Jq-c>0X7#nx>4j(haQlSg+PJoW4;6ANb*UN~@k zapCmQlZtv+XHA_keQcroL+?R_MMa}8F6?3TEi5V?KV`K1e%o^oYfc-h9KUhOq|p}_ zS?ZH=YmT_R{BR87`P)6L-ZL(;_)GO3mg_g@Tk~>YIgYpH;G1(!Ql-rNys1{NhH`w3 zuKfKpo-bE@K<_W&=`uR>l1^WECA9o5UET@xR;uUl_tObu{O;vOJn~D`fkO2&!Mix{ zL4W!A4Grl1TSEm;|1iG6x-RsqFI5%mR_gkicv%yG3tE1c?&PZ(()&mGyLz1Wx%TdO z@GYPW0=CkjbyD{a@J-yc^Ha&Uqme!y^}oMb(fgroY5PTe_~iGYmFQ2?r^jaxO>)<# zgM5DUrv@LsOS7)0@6f($k?OyJ>uSFLxhE)nYoh-F(s$2YLRg%n`s z(huo7w>Mjf^)qR|8T8)eBRc=~R)V^krrrxjwzU#>P#E6@Cn-Fe-qYZ{afpt;kw7f! zmjWB2yn&uSx!^u&zi?x1zX(4cO=>{z%TBCDd}JV65}R$Mk*?5^-)X-aF-|hqpVUG0 zuU;;n|9)*(TyJPB@ljh@=#y3=@^LHdp3*t>o?PlRzSR*w&@La-#qtt)#m1^j%kTE8 z(jR{Bbh_Qj@Ai^M)D_m=I;ro@!VTy7QBr_>cF{ z{BzZ#M*g+dQ@;8!TgdM|oau}A`iYz9yNE9NseTxdrSu&Lg&#gM?e8+p_6FSHP6>DT zS7W=ySC8u*IdZT1zJj9#^2Z}T(fh~u(^nJUVyj({uF#U-nZ6HU94ns31LsXH;{3<+ zD6~@+xq>U(vU2aoJCH z4)t40G!vDlQQqF4mGb|!7t(e|cr%40QOZIcjFD+ix ze7J6C^j`}ywAp_R-{5R@% z@s2KE?AL5{1h}AYen)y=`u?_=zu!0Kziy5BWw_jdeGa4V4ik13eRI=-#>oTeRI=&bY7UG_U--nC730 z{+iCe)*H5Ys0z<{xRNXX=1onPZ?X3~iid`$!^!tLikAJ;VOc(&xGWvceXHXE^@Bj) zch&ffac;jt{blq~)PJo~`WY_HeDFJH*?-NMz9Z)9^tBfBab3^D+3s}D;jO42BtN8n zWra<2W^)`luUVfy(ta__)6DY^%l=`B)!C^%L)&b%4lZcfZ_LS;njf!`ey+p`QuRNc zA9O!&K=~|fDHr$=^qL8sw|SL+^KvOyUY73PB;M3~xNd0lZ$kMOY)1YTtRlXa{i@EO zdGP2$?2c8%IJ!^Eas>Tg)>3~gxn1@rm>Cy!Z!u<8abZ#W&d0Z(8ZK@>W%T62 zlcws!Momv2H);$WIBLf9N$n>TO`U>04m@!SOK$Jp;-IIPI+-5OCWnh>wSZ4}>0ulh zV4YKSxGul>`61a!r{vu&DgQKp7t+pe+3E;NA(uXkivXN=)#*B)*!BAIo?6|)?bV3S z=dJC!>-`h^^nC|@JqN{#QC!~FZ0|ATL-9CYx<%gO&3>OV&P7M#&i zTm3Tpe=T2cg7*(Gm!HCY3ETf{^hbYrf68NiJU{%nnexq%Y5PaE+vH^6>z~KLr_#+Pf`2P|AmxjNUWqpI5I6vyyCAa1ZDNSe4{e$}R#unx(LA^V$G*?KG{lCS0 z+I|gfCuuTD*DK=J=h8eHx#D<)>G(K_--2=n+TOBft+YS(A9rj}tuEh7>Y*B`_7nSE zTg99HFWXP#GE6HP3q_ZQp{l(&Mysc|oV574ET6Y7;C7Wzo__8QxQ;q7fbBT`u}YQH z_@!(3cO%Sy^U+lP-#333^!*eZH_gy?wi;?b0smq6{lOk(jSm^#SzSaO<3mOg)kT8hQl)7mpjJ)`oLDFH0>|j7v(ZcE0N^(PpfuM zj?t=f2W@B66Y%>>d${B4xQ;q75a1|W$H4X*7CZe1&$rvQ(2ln`o`1^8Uz_Jqhd+a0 zJNr4F%fY{r{SEyjkd8Rc)Hn`$>7*vzocw6N`ZxK1bVs%H_QrUfzdrKg>+a_x z%*zxt+Rfj>bKMS|UO6VP5yp1tIqz+{*{gr~kp9}kt>1gAGe|$FMt+X#zGpDquju>F zMB|o9Fdk|AQfe0_!E;++7LAQY^3G_g&n|V0=gQ$YZIj|Ro~D7HYQF~mj4BsQ^Wv{g z?dx)}9OIKZ`N11oLgVX^oL^f%-`NdYQoX5Via&Z&D?Kd_Xpk0ew6FUK{h>&1V2 zNb^Up@U}nrqcj$&lm!hnfA}^Uzmx>_53iswi66h9v00^*mmm2Sy6vCeo$T~~SloNLGE^3$rN`Tx%?Ea8~UnV zpAPo&|GUp-|F3><|1YJyC*`b;Tpn3e9+ANRty@|NDSrMRqis^k>;Jeu=$n>Y+{S7D zuH1hmyBqPZO5cAYpW4bKW!e91wr8CCg{Xt*-{ELi=KC`2H|;K+E}`n$e~>OX&eYhB z<5!*ZNKGd{zCW(pnx21Y5S+8P-fn+pt5cQ5?|1+5d+#=q{mQIYnA`}18~U;-ty_Be zPm3G$5z(8q|J+GIXFD{;6OC@KC8SiR`d5g)7faB(l&MtpljDb$(z=w|GE5&8me9Hs zjDHZPbtyCzv-;jlyj@Eu0kEM>`)|&9V27*x;r5^NXVUZg_NwAeE;r)4Y;`8n{_?uy zGE6J6kl$~j{~AY&BK9lPb6~$&MtoW24~~QWbIEz0_h0$`ST2c|ZKV(P9$CIicD+ZY zcgfE8$g?JwWo$_4iSYzUW=9s1}E{J#;uUlvd6=a(SIXPw0Rx9+E6a30sejzFGcU9SGQ z`CeuuOS}k|0we#zAXRA z*FTqfURv?|?IWfA?WWoeCH#IrPVe_44*qyK`Af#?^O*JjaW}Z*!C7sVj*oCZBa8X` z6s{Y3+PamVlg5N0+0VT8Sy@6# zs=PddzG)^guSa$-2CLLn==$V%M~L%4S5XPx5M%x7yZ7Ac%)b`r-)CN<=Z&{%o>Tb$ z9$Os&zlS#ce@G|o4}$v%@Xv@O`ky?g>qW?iarU?PgZlm(zyUs*%;s87`gCFq$v>0* zANjXe7R}%Gb#{tBUQT}8f9+!T^ZSdOucOzx>!rVO`Hy(`g$vRVFt`7};{=RwgV?GNIS%3{aZbXfYtX!3XGd;CD7^!6A{CNjau4-RD4D)^y4 zzV8Q5|DVJB_LbiGw+9vD7LAt!e_cZRV_oVhbbZeJ+>iEmZoi4@C(iJhQFqt{?Fi)a zI{0s%_}eK?|404pU8;Y^k8_m&-`eU(_&v1g|8w#DV)*|NhjIDI?YHp%#vG6G-(o&L zh2yaO-;q!iulv{2WKa8(YH9y3Cx0Qghkysa^B(-4`C-3q#&wzb6UV1C4&;bi8hp4% z+CO3B-`P89hextf{E@x3Is$fwHtp}1f5%pWDDxY5xXhF`%r9;_?x|JT zRjW2k8uo9$h~6J{b@>1H<>T+p{lv_6wz)rQ`^1ZMxmuNp?#$N>sL^5i{OK0U6tZi@ za)qcq*89%>(*9P=ZU4?!Qu*0E%^xo(e?RtTtC{Wkr8DvT7@`|BEwFPPv^QR$AL;qU!5>N5>QwL>Xw!cR_<2)2>TZA7Q>^WSc=$nt(<|On zrwgvjrIM+Xa&6n-B>H35HL;3itpgj58&7W+->1&ifO9MJS#PMbE z2lzsuJ2pIBy}!siitWSgS2L*pr1+P^8-MiX-`7r`a2TI=C+Y)6|A8G`&Y)fmPV>E( z#pm%;NH^$TE?fPmSN&7=PnApj69I#Py4B=ANBaBX@uy?+onFW zdcN!a%k|HfW|RM)OpOA(M1Hc>8vGtwUYA^8|3E{Xjuww=uQt-ZW6xqg;qTdz{%_hk zK3ddA?Fud&XKEa0@rJqM4|la6&gA!ZEAOWAe^(Cm57yeud*qZE*zj*oS7S{sa5i|MC4(K{nNYdZz6!ezDb& z;D$E&Kd7Vm?Ni+K=l*Pevz=-e+uyoHpFdCiivPcS-h}>n>DKib*ss_uIJ*Idpg{?X+U?Efa;ug!j+epdkh!gZW@`tUJMezZSdH-_4uf0g~q zwf{tZwbdzMcWBf8SF%6Yx4O&E-JC9Hzk>Zxe*nMb2hQ=hZs?-#n_uHq{z!hgSj;Us ztBLgd5}nD76IW)5$ffy8I>_glt7yEjOY)VxMfqQjk7`j^%JbAdEuVKn)AE&|yb51N zb5D?bUq{#f!$lM(@mp}+;9ouDbveKBk=P&gfBPQ#{jaG1ZI?c3Q-bDGcea1ltxMO| zz6#ALK@Fd`v=@P9v_FND6pk%zFN!aw{7>QVi|uKCy!IkO;RJ;vo7xL&S$lCc^;ePK zeMDYrPw$`Fi&e)`xC5o?%k4#yw)-un|6IH7zKu@*LHp0&lz;7B>HHhtXRA}fZ=mIM z$z_=OPkH8PE)R|SmpxYdVRXOtE0i;B+p>SI)A1Ic$4_B9bXLI^UrGKre=m;m=Ye}D zy&e3K-)(hDa6`Yx^Gk!@@B66!)wx`f1I^QtN`JKX`ixhjc6R(j`47G1t*v*v?9b)@ zE5t8)r|ob5VXL!%8@jyw3BE7HpVW5t_40LM?FVxrSWIDe;Udk_|Mw9`gd_($io3X8r;y~h1*7Z zl|RX+^$DzQebkENtR@be-_@#LQ%IR~ue*I@Hzi$^@?%N8no7D7m~3w-ZBSE4skM#r zYfZ}k4QndNCws~5@#AX>DSk^ngXgiHxcc*{PX8(G5mblvIkA!m?W`&)asRH!U$%;e z-JvUSQ33le;(jKk{STX??JPdl&$+OF=ouYvKgRhQhV{7s8WvV9U3q)SLrZ^f^6z8+ zA3m1KpW$S2=C}X0)e+!^Hu)QJ{aHkHKINYgONMGr_|E{|@WpXDKE&6ht%suy)D}nK zGC%q7*JhG`x8eU!$@FblJ;g8nvDJ~_hBoJ->bL#y2|o@*gc=G zL=Zt{H5*x^cW-KK`!p|^Hi-bvZNxwZr9Z<62IpDo_|K*!snmpT>v23lU1 zT#$eL%1<+0;6LB7|Hn@$-~BhBe@;N(ckWAnOa2_@7wkXQeN_IB;KE%VQ^}iO&PyW~ z@HgW6r|JKz(a(zO72rSA=ie>*=R} zk5u-yzsX;>MDv82y35Z>%$ZoDughY#n~<+}>iYux+5=rh*}MVe(AYbp>idP|>_0o$ z{>AO+`O(2|SNYHQV|cFT^3!6DY}o(uek!IcejEo~G-AYA(*9BA$MbvQ89Lo*Y5x)2 z9~=1=+O+>U?Eh$wQc(17xDgNkxr6JU=r3KC|KR#?{3Cw{T<`Bte|wqoZ*)-&aWc=B z8~5Qq*~q`<^C0*;fz#-}^eErILY1}uSUA8(oBX?q?r^vNaQ}^Wh~H{dJ;fi%^7bE- zANOB8&jQ1||K{-q^Zsk$03U5`@3(1+dViE;|H1pm{sXE0_C-}O8TFrP-u#n|^2h8? z3yhb!{ES?u?TqxF%I%7YqjY}vuX*Y-ncGi)`sI4`{b|F9@qOaF&uITamGSd5aYy<1 z2XZ+HPMqNG&bnMed{y31!tr=tfa}6>rbf95{YBu$WnSf{nqTYoY;hkykHmfW<-AsM z!Tn3{_n(+IOmC&G)OZf!^55bOwNPK>c=$JtGqst&C->{6zF!Nq;QI@|Kicyq)kj8E zOYui?yzOuDZ{c=wG5uuMPxs<+;V|D0Htt1xJAXG?mi-+y0(FYQ`t2^NUAc0@l67wW zI=hJf%(Va5hk5hAZ}|V+TuvsqJs+I+8}T@E3EK_(!>@3hsZIZXZEBy8m;bjUesLCl zh1RPd8h@1SiQfl5;=u`i#Fu4%+;=TLahnTgMV$VR@9#gM`j744kC&7GzN$JM#YSD9 z1pk8^Z*x4#&%yX!n(qt9mpIPU;D>(n%9>Zn_eY5*_3zr;|158!K2pQm z{&IW>xeU`vtY?3)uF~ZsM~i94>+>KU`S3EtbN?|52l!~D4|I0d-k*6rzr=py6U6$c z70KxwCq&LSBK!X*FSnFn{D(*jOGv4GF6Eya()}Z@7)13cdOpGP%uE{B0_##YGWPeu zPu2U8P8VFW^-<|R;g+24T>rm(BAww|M{cj>ZU0C|{Y$XFU;Y2L%KH3xE`a?Hwf>*a z7na9*Qsci>`8kvM(f+@GH9fyvSWQH@e{8&*{J-~cbMnuKDt+DY;J@Yy9iQOm8Vd*b zX!s5EryGWT;^jX}uh1umT+mysNIN^*pWmmVNzU&Ty|1wn%SCI8jA=SNIu_2 zXv@a6f!4>VTdgP&?Q#rEHZ{Bo@HpXF?S)PLms zQ1QF6#Ye^3J~q$uMn9_-XgTkjT;M6_K&;$QT8vK)#xYc8bt zE&03w`S*_+&00I}U+DiZWgq#^W7Wl-+&@23$J_pL-aEO#e=L5UjWnPOa4cK+{uTd0 z_tSv=%;z2CYsaCkRg{~!j^LytRQn-zLi^8SH&J`jsKXE~?7H6k7(WR7=67~Df%#kj z`}N}Y8YovS9N?o({x_C9uKHhLcksuF-wvhuMZNz&_)%_vchd_mc97$%1dZpHdn43; z={zdbc!`&je`!4~&#SoW4@Xbpc&^t~;`0{OcgLrH5x?x#@EiEqnrfGib^1TXubD~? z5&fZBYJ8_i{qos=2tSWRes%l5`JNj7zlqxi%6tEU|3HsiQ;_fE4{`sEpO=#Thdbtf zJfeL33)r3(*T-Nz^E);8|Ahs7e>qv-7hqo;XKIThVE?9Hotu#SGmqx;aQ~(DHxvIa zay};=FB*9B2WRRNQgW%KCCZJ*aG`Tgmw^{ITN`g5XReI;MipfJr(8y!$zN$@DD zUt9Eku77{|`e;VyivZO$7FFCcokm9$%@8M@l?Rv*M{U7D${=xM8 zH8Ncq*m>Umv#P4TPS}4j`+w*~_w$!oPllhY9H!G9`4?P%{IEOp-T~ixj=!;t?(cH3 zI`me0{qwT_L%QGN25N4N9+iGRnKzH@P2)Euj%uM~9bJ%|e^<0@p~UR3zJW}GaV=%N zVRuVy|3q^-p2TkfH}p%}Z}{7*{$o9_&tPTi`j6&Mk00_)W9pws^BX6Bp!y6+{~^@h zhWsZ&>r&cYqr>9I=0eKN^gNWf8K`KxAWe>TgX!;E;OKkjST-?T3{p}X=*KI=!H znBUTwfARiw#)FjJYp9UG(!`tp!9{up*#F<$o+iHQgCmtbu?y!@P8XCTXPn}WKhAa3 zfq@`L*{pkRtsivq$NBwBw_0TXQ`7wM|B(MTe$Qz4(bon3!F*kz&APmTeGeqpG4LOx zQ~h(=)p7a{>Oa%Ir1ItWs;SdNnwHQ07{3CXI6)r!58ms+f6!kj#PvFG;W$&9_Mi1e z-5b5^{{`KDBmL9%w~s0xzn}m3*?&2|7qg!!pZ#&&xUT5DNl!`s-N$G@ME;e}zlk4m z#Qbw~|1QDvJR{#i%Xy#W0{=(*vvrBP{)6ZDh!KB1*FO*S`4{{x$4q=!@+S>`)&4-` zC-~$4$NY!d{(y8CID6tTUiYuZZ_!t1&*-UEnOr1l>{$18z>4GnJS zUfI2#_2U17`J>IX-(Nb+-TxE)CqpgT3Msj@iKi#cuimn)k_)Hk`a$5BwlsHnThY2M z<)<8)PnFiCWY4GTp+>A^tF}rOQ~yb4`>27P3Znc0|C_6ClKlq`=>IAC6BhOVe4@8H z_F(*pN^bs=Ds6?6zm&GS_$#~lQ*tOT2}5 z`6K5=mkaXmjGBBudc|G;JdOM5;XVsHCR*wJ5pSup`Tqkq^hp;ls_*4Le@T8?qQ`6d zdyRuD$GJ^zrTJn>mQuwf)QQF!f0f3)B^i9H_J;s%lOpH4mGi~gv@S)C2Pgd{u!Xjh zpubRp)}{C@;D%m5FtFE)f9fCl455O9^T)Q+n7t%zjv#*7-}!AChnEEW$+zhGNWd>> zUn$^EmeO_-@JDH1DSivm4cqPU*QxQ-!mZuqfAKQ15BblQZ8}~YKNcYSK`PGtYj_vRmH z@c(jx-amf1zHj4){xRYsydk@*vA@BGpxqob*MdyWF6hltcU4)nA-J~$ofN~ zc2yCheWlE7P22m?`@DMfl~fr)+v)fu9V^9e0XOtZZSTFoi+}!Ke1_TDE}B2xep2TO z@~Q@zmr(kxqHseBli>a&XkRHW)S&Av$P(ek^_2`gnfgn8 zPwyY|C`{tFz;7(xVdE>`T;%j0j2|^FOnl*V`4eyF?LWuUdk(q4e=vS2-lyoTLbO0W zJwn?9{xg-IU*K1HIKW4n{I@m@Uh2ia@q8+Ova-Zg9rW{8q`f!)RULGJ0sdFf{%pMe zx|HwBg1>V?`rpCt3(Cu%&(~$KZrAJq)&F)m=OcW-Q~g+aKR7LIf4hS>f6bn5{@f~@ zPcL`hf6ew!*gwL*i}p`&G2Q_R{J8%2diPb||6)hnf446u|CwG{EbXUxL`QG_rF@%$ z{V(PFSE!@={*~bOO1S^RPe!v}A-*hra6>|1oZV;;VK-%6@7L4i!`V`^$Dp)=}7^=g%ZKFEoA@t=J>t;`kVa6Ise14+WvSW42}68VE<3(#37phLH0KZb*d_4 zKNCUPod4n7u6p~Z{&XKWql#EV@3o?Ioa~1Zr~UmFa6=!y>(S+2{0qjBr(d3)zam~v z{+?vV#DFT|L`n~7|JNTTPMWtueP2THn>*9?q4b>AisDbLA}*gsdSDf?k@kyHIMjy1 zr^$=zwFgh20Q&9?Vl$fN9E^)!^CUVv>!)~^Y;JOs_8No_TP1l&hMeY`aT7U zB*(`&9_7btr|SJt{(uX|nHv1i>lXD6c-8+e_=nQ*f$HMopuT_EoxS-l4pKVN0{&iH z{@54me1qRV&4>qou@PSue+VaJT{^h-QpsP+{SVRpc@^=C-_rcz_(S6tHQn|HKjJO^ zeefgR=6F8^yF+i{g+AphtmAEg|w6V@p`j1zn${MmT^ooK{^ ze~J-b7Jpa%oqX0iud0;eEPwF+x<2vSSEl*J3EupFf92+Xm-}gppWXhS9H+|x)YJ0# z_g|@{%OBJ~Espn7CV%a7|8%{7$N2fx$J6&y4*qyK`L`@%9>e~E+Xv7u1@^yfuD-6& z8@ik>i+^S%ePtomJHpS1*Z93Qw?8a?gzEpfS)#$a zY|rlA{0-i9^W!~NsF(Zs#cIoTFrI5pzgEX1Ux3SxKfoKHzgcyR>-#~>-_(Bx)nES3 z5xxG<=^ppt@AZdHaM-_jHO*ta@2)?a-=D&cqd8sdb98=(U-KDCNkZ+*ReQE8PUe7PX$baM#I!SX&J3F6W zhEYA&YFky5)(R-Wytbjk144?d|3_$hh_*|?^J9?KrJ%l?pnaw6qQ_7pJlMYLjpEQ_`G70XJ5 zKfHgwpW!(kc8Bg>^Q-f{_@6(M+EX9QPVvVNkw5fm`RtGKKUz-v!|$Ol?z3>Wm;c{Z zr;@tjL@$5;54EgHbL0h-!2e4rEJgZ%oVJ%XpfCyiKThjX;QzrqDvwCu{~=nJ;{(e{@?r1{3rBL`Rot>7sJcv|D}9k(Dl}}o9yK8+AXN|ID0?k-`i`X z&CsWO{AJz0qA&MW=Y;)m{}R{e>)n$6Mb9<(4fIV-!@qd>&*Q`|7pt$o|5!&;f0$0> z4Lh<82^b}q5T~8OZm^W z+J6x5XMgw&bkleCP4e=eJzV~TPVx7j1o=m-T|f!^$8JylK_dOfK8DIC+AanD6QFe| z@SiyCD+T^jO6yYm7Hol@Tff^$&io7ik^IqqY5$4yb--_+{rt!J_|VFqcsczi#1{rV zYu^A>f5!EW6T7>GIPaP(J=L%g;gg z05AV}m7c#sr>6Z!oLWBqRQ>IfL-U`=i1OXPkZ#a*A8**!EB{J zPuQC3yClfJfqcrJBr^XBsy_s1n-t_NjT?ET+Gjp=#PP4T1D@lXgb{8Bl$_4~5Ar=xG(_JY zUw*t2W$!`syg280CF;FVR|;Q7VG{YiNYM7+yyKO~_eB=Hhl$NRUWt5f6d6GMUFROJ zq`dr}pS%4iYQH_;{pnufw{J|_Ukogt{d@5H-}nTbPr)&i-sdD@@_lJ%C1H9$Xs;vx zqp*~1w-J9Ax?gnSYa9{-FJB&?w?xo1OBXIP;_2hnDFg7nEn; z&jiIl@`~#Lft+_-BtNu3xfJ z{Ey<$}FNlWH;7E-bcziZ=3iRuOKqk7{3l{}7E-`(^``V70e`d? zm5(IgPtZ0g;13h46!42a#4g2W0XKB(!hhcM;@{2ui8A;D{m7mq;4eLe@-GSagS0LM z{3ZR#KS;oD5vvsN$7q`rzXjaT&6*WH;KjeIueN_=rmarpXMb@T{T2!Mqo zx)kt-26m?Y4r%^UVwD2^2yK(%w}2b^-q{OI@Z$f2`K?(8=Pw;hzfS`GaFG0g1pLV% zZk zv@Qkw_HZh1NWd@7q3tB#579O$ehavv-#Yi<)?WN|`f2-%*$3wjj3j>`0l&C_ewzgR zk@M+#NWdSbZBoD=Ayz5iPtZ0gehavvpM2xU$GrIe;qoV12ERR;>`4Ot=os>667XBJ zE(QFdv1DHo@QYEzN&@}}ZIj}+;K>I1b)=jNp9Z}z#p z)G1MKgO+|J7t}w^dYalu|L*Ii&mSqWRTS!Pj~MZm+Uoxgc8Bi1>PR{NS{Jkbq~w=N z;vCKG=l|iwl>bOz|6~c3HzcrsF0D&}{bNh0d?$hZ!;9#664>9miPDS2Z$Z5oI(hz8 zGo<|kZ2wq6P#t>Sw{-9Q>9CajFzuhm8%+D>;{YFxbc6nJ+lgBx zzuEtA?_7F*DM>$nMb6XwI#tsBS7o|5pMO!G7rS+P0xd~N_fsVl4m=x3 z^{343TOcA7_FG_gT<6!;P}Tnd`wwIPpGE!?FR3hks;=8X?BU+_|EW4XxY9C=PtfLc z?dKNHN6gV;JNr5M$-sZ3JpKmaEgayZaUAs4sYiE`_TP7;{$2FH{q-h#UajUB|7wIc zzu)**k-6^YNA!y@$Mu50g^cf&eDG7_kAZ)}Q={fdev#+q-~1uHzspYZ#}AR;zT&^b z|3ckL$4LGhga5Y`^!_L(%^w+gX!aM8{|^83dpq08{ye`Y{HGdy7an);+vgt|fBeq> z8~$pe}`znS)*1dm^|LmVc=6O~i*?}!V$`J3zJxm5m5jZbiBpWfG{ zzds5eOWzlf;QOO^$NKa=e0_oMk4kA@DSnG-|HQ`&A5i|!2f+R}5`Q$5w!aHlOg{My~o;qg6McrFmGJ5*>DaGwOAJ=|rxVZh4(US{LnyQZ+H9dXk zsA3%3enQdIDcHs9G`sqo)^*8$D@ASYCV)?Q?utCn-4S4%VFX{GTX4pW3wcdg;Hrx&DRzr~N8X{iU>; zSXr#g*T{w1f3;T#0ll(VuiMI7L~5Rvh%x@$-P}(+!Q;2#_ZNRaK1Sc6%D#7U5qotw zc0#U5*27+Wa#AR_5p(kQZ=NK-KVHyCbK?Eg!J{D3*d z$dm9BO5?jgj9-N>gy7eKIj!iqwWT;pjw3?jnuxirgmqy{*EwVW_b2GO_uufPwEsBN zU%39hHA!XPjoIRk0=~gt{xa_?k`>DROWkN4)Ee!cV`dRFIzu7mO9jQIoaHR9nvDgXbp zIrSH6E}qZU`9YUP2acl$|ACIqDbADpxjYUH#=n2>cLMq#OMH2==8yaEe|fXp|JQRl z3_s_~5||eq`P$@bMvV$p)P*{E%QZgoqQag(p`BCTke0M&DD}0J7E6CuZchM zVcLHp6TIy&$Axm{UyOei8E)g0N*_~0r?24o11;WqZcXB)L5O3$=)2v|+KM-*U=2|? z(Yu|<{k*N~95P^W4DJJ!@{fMU$&cs1{%2Ma;=44z&EJ>cz6CA6|8Vk~-_!HYiF)X# z!}l+ge{$Tb&=E9G-`5R9a!rF&KaJ9F8qoNC4RCl$Tf7GT=TA%Q_u@Zh2krP%nqN%v zw*P<1e;NHu{2Jol*+2x&X&`Q&PWK(UABPgePhoQ@-)_egX4f-f^_!Y%#$NZt(Y+e(5AH{+YGi z{Nc&k{@{i#i$CteZ_U)#b*TKgj0GLO;DzQ+{&C%d>Pqi_nfzZZ+WvtlneqGCKT-yN z4$Q|CP`Qi+Jt#hQkduE6`_Iw|ZvJEi^W(bw?4N_52RLi^4RrT88|OIrbGiTN>xTVf zQ!}%Fe2%yOTim|s$6sFmfBL@ot4{s^^B=Jk2GIT!4rj)nnCHz8`ww8dmN)+{=l!93 ze6)9nlfNtT*B$ES4@}F9ADlV-yC{F`D-WK3OL%`%As$*R-guFdKg9f{PrCV&8S?x2 zkA3yQ`QcY4Kk_Z~2^Zcu%E=#Ne(PR0e{6ci+8@^iPSbzNn}2sbRq~0GANQ{#SGxJb zMHR~*T6l2#Z!rzeVfa0CqaKg8bMo(C`=4dx-#~F@{0U>exBt}tQEo!d{65~+$)8~U zZx_1lpR8#9>oc>z#pwVY?RM;GRjB?G;`RrOKlC%@-_T#_`dfU4wm)u*(DHQ7`sWs| z|C{Y63wv<=dAa-f9rL1;{;QS9B{_i}8KYtP?(PYCmVf-f=l39+6*~XgP||7tt~`ET zn~2-~;Y&0>>OIh9)qi};&l?YJ{{i@Uwl`FmSD+`>-WPH5FJS&j-{1sX|G6|X{`f7L z(=7j^%XEA`^O)t&H~<^Egu~Fq_xyFKlRw7wpC_Jk^CvUp7t6i*LmWTAhkt>=jUc^J zRtt2S&|@u~{L8uiIk_%8T-!f3Gei6P@LRX(>)OI*GVSm9bOY#`n>&7#u%7g=JpM3N>Cx1@opgN@OPj3GST$UMsf;n>-Bvt;e z(DpxM|Kan1zee`6TfO-2ecR2S%#c6IoF>0mnHfLa0($#5%Qrgt*R%iBf5Ximo1K~c z<39dx^Zm=u|NWl7O4#_#9<_8kZJA3ytBd_mBO6+P~i_0ObH|IA)X_0PZ4^-nvZ`H`!k<>|^({~X5W zkKE#}e_|eh!1Jv{^to24dHOG0D|an-)gRFQbNXY3|6l3NUsnC!j%quD zANd#gVgLi0`FD#6&f%D$&;InUGoALI$>q;=o8A0@`I+%Y%iz!V;a}s!kLPyiL0g;l zbn@f*ea%yD{$z&yV%5Rz5C1pqU*f|*jN_qy%g@{9isw_1AM7AHUar|&y%{|Q`EvHZxt ze)cc%u|M()Hut@E_T^4~^xudX`8Sy%e}wIv!#{}f2kp0c9G|j3k0WmZ(9ls1Lr-Zk z_kJh;0`~t&hW%sLW@djse%Sv2e(uK%|1j_c>dnwajrTs`r2Pu{nsHMK+|Z9sowV4C|7?Rla6`rN zqh4zAi*^UEKL_|q@>%cvXUq#ue$=1uG5C`i@>|<z~o228`zgIQws6W z{dRtFo|7N`^Mk=3Ta=moqYd5lH~5dQ{o87P^Yfn)z7pu)NBpqL$&dGs&l>ly@QvR5 zW!3)^Yux;)`)`KtAH&QKy<^MKvz+{B|9{*p{}*S*FCO&f4{<#F*t~y@V@^|n8~WWB z>NfD=Urf*MdeQagWQP2qC%yUO9Dk_%B}@gqw(-ZsPJXmMPx#2qA1les{?=38{1x^7 z`lWv_UhCw?{p&rW{WiQLGk(ANPlT^)0o&8`AHVxoDen*c+L8O8ck-kDaQGCr{R1~u zEPrCQP8UD^EzD`EBu7A>@#CJ;o&3xB`=@2MyZMtD^83BNOR)X@{C~Fz&f&13PaXC~ zj*~yi{BIliH?}l0`y-#`@b9AhM7|!t@uvTb<9Jh{-UfYhvs$kA7fI&-$MpZ3GvxQ_ zzm#eJ4b%P)RjTW%|DgQeXFR_IZpn;4`jU6~BVN|=`G;tKi}!~I=~pLzF7vNlXWC5sQ1?yUWRs_UDknAHE|q{-_T>%KzQWlj7(0=cK`jV35rUeb|iQ z7dZLRe*4FTZuL--WAYp!*J_D|J+rs?=x=0W`@ z&h64Dmkjq27Dqte^58C4|4BSQZrxVRb!gz;%=qJf`tW!2<`408`SHVVpdY_(*}uH} z=V#;jE9t|ZasBOY&3~x=gK`u4#J#((b@C76^5+I4|Hkg~wtrdtk^OG_ zzx!{Dy#l()rd=bQ{A-v$@1jb)r!IfO_h-iMSN@~@%Fq65T;7`wfE&6-ozQeAKl=Y% zWbg-8XT}fC97cfsab5WwZtmuhyNgb28Elvw)Xt{u6l6o4+jkhu-a@Ol8_X8qnth zXX^g-x~=vHAM6Z03@fa&ci!@%lOOiqG}3MVqz`|Y_UHE$;7{3~zuz3d9H#w)1}B2K zj0OF{M+=%c`MYxa+ZSux{IQ3$y^t%Q%i@pM@%A6FCIrw>ivo7IodC)&U zc=G}me-C}-yGn5aF8|k7EPvz(eO-S1e&6pz`8uFS)H`CBlOO%B&feta4?LV9e0{vKRkl27fX`eyefC@-JumL-*(q?d0S~`%m?^-1d(>l9~M@ zW$+(RezN^bnA23H90A?4!A95nQ#`*MYw(93&5R#@VwOJ%zODt#WBSh)6P&|ABfeSI zvm2cDNBOz^Vz>PRvCR1W-k+jglyCC${cC{1i6GJidRf2YZgcYE{l&7Vn?IQ;zvb=! zXjcKJY5xTPHZ-`QH$1mwf*1enN8S9fbs5^;mM)_%QBUr*%(5u@lJjcl&<@ulY z80Am+vCR1W{6E6{u=4@-hxq``I>zzPd-E@JJ-@7H{@&Yh0)77q{3|nl_<0WhKI#vN z&f5O@9B=ZcZvQ4fu7mZ4T4ODz{o~C4$gghxWCiov$Ctrxa3h$@SkT)Z{oM8Zg8nll z=J-F4XJ-E>bDH+gwEsYm`Jp!zuWsYCKiWS#WxKy$2tSb-zn}k$0&Rak{~y4dusd{& zSD;(%dAWm=AO5rHb2oqB$;|j8KF=?SE}8jHDf2_O-uPiHCqM4Lp>g>BOXuHYhWvj1 zW1nzv`)`544GnJSCDmIjbMmAA)sc520X2W@sm$!}=RbD$%=k+f3%XIyKleHLcXRo3 z+3Rlp@cPX7<2(<&nSZU`njd~{mY)mo^M?Jw4SnT@BkytYCz=0J!~TJ%E0#Z!AwT>E zdgq?AT

    y3Fu7Ey7`kC^85Kuru;F!5a`2O*Z$OLf7Cx;G{66iXJ&st z{{g?B|Ez(*4UKXKy3^~0k2v{}e~XRsC;UQY{C@sp^~eolVa*G!_rGZW`TTu2 zAV2@Um>IvH{n7vM0Q+)zs_2>|)PS^PY z<-F-X;{a^vQVv76zU`VDo&50sdyM;UGDChp|4%Ugq56M}`Jo>f)z#I14E3K+cf0K$ z+gh>qNBgIr{nr?FM=;DQ&=-&Y?QEz0;XjLv@+bUi#qwK&GxMJ?Hw!{H`?Qg({y&r7 zU%za$-v(Z*SpG;^{Aj*};aQh*JoHH)C!ciLpZVj4{gWB;`}sfkef{5uAAS!#=ema) zI{8bv{5)Zg;s38^W`Do&hfyy1@%#BdxS^+f(CK0)Kl1MwgFpO6X8hpH;q*uS0oRq! z@n-qy$B+G?v#r|-octI+;rc~J|DSE%{AKn3@f$x4{ep6tAN_yq*}7kr#qp+Ez!72A zA6(t}C@23u_McNXpofg!F_|ep&)1v7znAJiHbs{|c^seOzo3tbu{hpT;D^q;cHB58 zzs2K!t~B^#Z)RryICGl(HglTx$Na=IO>hqHGxWB`zg+L+4>14d&${g&F8%ND+qr!e zp&vi&4&D2Vb=#f%u)npy@c*~|JN${By$^}MxN@VZUi{%F-S$uZm-wx1+W-CRU&{Ui z{q1ef4tDbA^7x0_jrv<`ySM$z>i>pv68?|!6ZHr9fmwcTVNO%!R?;!hEt?-Z)X9(U z-=}JS8Ah^qe3-tN5J`}pav z(E4-o{Tfi`A8KFAE&so&|L$KrKcD56_Q6f(xAW7_?_Yh+2+|*a{jbrVZ~oaF@W=YU zHY|8`J0X1K;sc)X)6e?1(w}Gjtx-JnKU4qw$@*>TA?;80JpAxn_pdr_ zE&c8W+n&fhn@#cK`}*VF##l4l(>1KrgJ!t*A%=S=({Kw^Vf#-K?$~hn16qk2_dM3& zSzsF6a_^?QL#YS6$Fde~fiqD3r4c`ZdpHGXNH^`ZTXi@z7ySi&Ed8-E+n&oko48sp zL;skG#=G;=^y^~;O5VXeWJ_>oaqB)FG&>*X7lXf4t3Do_bf9%R9s3x8sKzfX>th7w z?}NlIZfv3s`~W+jWYe$h)W--uT zd3ruG^?y+e_n$wP?L)Y4jr5{$SVvp(r4fFWn&7|ovGZNL=alc+p!l@j ztH_jVI@$2tH^N?5btg-|IRp2I4(ZKgGWWMq{)yg)b;~9HIQ6H<>c7>Wto#Q-f90OK znxftwl;eF)XXyKU+z3N)9Q9l?P~-+SN&pskO9(7 zd+zvq9?eDn;DwF6D5)j6=_kGMWM3oj4a&Rl6CcsrgVRNSKz|x;0n%T0eP1JxkN(&S z@DIpGKifO%apwijjdK5Bo$8OP`;X48fd0s3jA|0^jP#zUH?+h0^W^!t_2_BBqsZAiuz`twrrJMPIP|D&c``8&V(ft9h@N2gj&?66p!_FKpzM z<@h?J2cRK;wA)SjVree=SFW`5yYKxm{iWyP-X8$zFRg|fK>A5{T(l`^v9a}>Ccv5>VN$;eT@M1-zo2H1gQTtcov}kyVs$70`lp99sC6H z>A$0l(4KPY`L~Jwr&a%Xf78n+BY*1(%>g5lY*bX zxzB6)+1%PV02=B&?XssHjnRKjF=P{+x^c-`=(KyJ?%>UwQJ+_J43?Y^JT3cWeiNI6boc zfs&G-e;pKtw1|CgH0a(A)vDkBlu-JXn(Y1Qlz;baq8H?%fBp3u;|i-kVfy3u2iwni zkzjx26U6tough`Ktk4(v%ou^2;P&(T1H${=i!TbMmyiB~#*BF-7yafCtN&*IF<#U# z{ms*sKJ30S0?$JKh2C_zg@us6xKkr6Jc@hW{rn78`PL$?zzV2%4E^i+oeMm8TjNLJ z7NDWrX?NTI!CATJziNezk^a{89!4GC2<5j)ru<%lt9fH6ukImb9#H>d`;{4i+oXJ= z{-`^$%!8iNUz&^l{J~`&j0Q>WNht3KE3h2trBjWKb5NNFyBq*F(x*=4qkqxu6LRX$ z$QNvk#35GyE&bW?%bR$A>T%G&2J{1<4#s%`^~w7F9yCRNI<*G$zS-Y{jR+ruyAE!F zVQ@Pw(Eod@zXu0kJmmC8rrclV!A|f`AU*STe+$Y0?WNJPGy?`%-Khd|(`+LCp zDf$KC17@rI>+mcAZoaV-L`K9_0N1?s-3Dp!pi2jctqm z^=ibQKhT^#7vZ5_wQB~NdZZUY`odr2L4-d8gHtu6oA!bCO{vZ$|64)7m{dz!el7i3 z{omrg1+{*urD}(yOdw*h6b}#~LZ&_cg{to!u-u@})lf}cPY^hf-o zop&$-ap*a;K1;;!$ogAsTg`7iEB`yGq`O@?kojJS5elw9;L)&mk#&-&?a zl%YS4ia^p&_1eJ*JP&zGe=Syy`~cKQ`9uDj2;L3Y2lRRFZQuq>RCi(k_y>Ha^rjC6 zzkoB9-`dfTKY%7o|8I#49?C`k%w28$nd~#li@M9C;QY^lmst9fKld;Kx14U>>G~c< z;M9Ruo~6bC&yKlpnEL+$f50(}{~yE$6#WQ(F9g58L9qZUk5~ENIVCtXWg&a;KS!J< z{r`@=ck+BT>-mRY?cK;EzbIrhllTk*fx#O*LtFUr&!6m7Xw%_2@*3P+d5OiRufsqS zhI)8*a}EEj>)~0ybNm}U75rb<&GVDLu>2o=F4ZWP{3|xwDDEkvy@$wsBLA%Z9|^Te z{)qaee@_oyJ;S=|;1-wy|HKW2CegU32b&IqJibKzI;5uum;4Li;VvEA(}NQc-qZI1 zJxv6#K;zdTz5q>_{spaT&Vu}h)1U3v_q#PR?uw?M(ay&w?zXmblFbMA^9nCYX~$@z z7I?b$I%A6Pqg3`hW1GfJOxCI*Zd4kr5@T&!^?G1m-KZ?x7tQiIO%0g3*@B&+lL^o&sEj3%}&ku)}%*aLmxP*<7 zp5S^9`RFfwsgn^P{SKZzQ2I+Zz#l;R)9@F_M}G~%3gn}|%#wu~!J36%PdQ!6Z+shD zjyQgQLG=H-LbGdQi+8`uU4DhHMhIG|=^0Ij^PEH{I%zo#%~KU?cTArQ_z7 zmM^mk14x)k#k2mg+J$yb4dqWhS3UQ^>q38VADd1>=MRsb&l4m@Ui%Z|e^h}<^t1FQzP9pjt|`$@^s@q+s`wu+#(N72&Fw%)|K(YX1CqNmHk;rU z{7Z1BdpFK}kNI(XTMtY_|3k*VEJx9v2-mBzndriQ8z`xo;*h;){KX&TD!=iEZNOxA zOZS-v2m5bQKQeE{j@N?T(+V@V+hn}xWcBybMM3wD^8KlO)_>+@LHFIl&)r%7eDuFO z>&4<+^dE)xMNIlo*RylcpS}(KTd*e}^rt|-i)R6$U*u``Di!w-K(Jg zTZ|ut{@Az0klARDkw2n8@}K?z`rY6A8$bQA-}(pWuSNI({qXnGABQ_1{iK`rHec@R z$0ukg0n5>eNW)!&XMx%jdIP&a1Nez%o8h=pUOckGFwe_nd5eul|9)G5qX35gy|Sx=FvI z542C->6+G3|F7+5>)~=eKeNrb_@33M$V@-O;!{7^asY{YhW2#yJN^CVXSJSps;%B> zaITC-m1T{cOLcEkpZVz^|F; z3;+1{=#T36g>)0!|0n&m`d;=Q*e;R&5v|MjajVUDTpVXkB7oL8iDjjt2$ylm#JR17{A1i zc=D87`akHjMqWy(hpqk}`-aq?+3!bp-H!5x_Lchi2;6)Y`wR>hq<>9Zzlxy$R0xoM zV)sP5EOv5vMMY{xCBpRD}ZZ=ii& z;{q?~srrK5$DtfoG&Z}SJWKt*?hec6zNp`m`oe!)V{__>u=j3k z9!B^i+)p0?e}s?q2Oc&J>8AbE#=Q!2(ccvN-xDWi=}-Lp_vrW5pMY-GqYa@yw$GOI z7b_#Q2R}9b^IZDVW_znY<|12$rw= z3H_q~G)#wbr+w`$N9J7rQ#jPh-x+1g@5rBn{hvzcB|egy`qmK-raoLX)n6nU8lW+@=eh1G2 zq`!2-5RCV;^gDPKpdsC~-`Mv1opaHD?;O>iv3~u@&hJz&M~Zz5P3=!ZJeVr=9NhIk zB0m70zKHt=xB;X$jb{PU8*c&q0hG26>O19MUkv>O(6HRlKK0gZDy95}$B*&3(*8Cz z)f;vE`@78t%axsTkTk0|HbSdbXv*{jojHDgaWr4)*Q~X7@rxRRW+w-aMi3S zL#9*>@eo5!8ajB!8?4$S8WbtLoM1MB7xAidB?=7u{eA}ZC(iGTofM&`wv`aAV5EHkS88F=HTY9tY zcPQG74B?sol_5O!x+;Wc4=ADu(!Q-`*ik}1-%}QedXMLhh5mHR(w}M|{qrxi^z+^z zraR~DAfEJJS7iC*{4nXiM#CrD+Ig)qO(>yuOzp`%dfY1Xmv^x8k0|}?wg&y3vh*h! zH%$H&+E0#kLIdP8x9?@sXFE%JmRx1=?$%?x(3I=qIYB(S>7HRc^1161`@ikUU+4EY ze--%>?aymX%#~W7xP`gtzf#kUtKCw=o6D`-6m2FqRr-Ij^5*-8D8FgNQ&0Y>^NPef znhijD#)zAGDW%Rm-UBYNPkXZ-q4`eprU@~z?w{VAoN8bw>^Z)^&Ts|3w`$I97>1nd8o zny5b99F%`~C)BgL?zQfezL|chtZ_|a9qi{5gY8;dGOqT8gWnh>>xXM}e9HQ#8P+!@ z-)WkmKhea>pLEl1DE)(!{^Ut^{EO)a&yfDwOR%mFz>F97w2T5 zKN+I`pC_RFR%hvVoBloe!|PYW^w(U4b(a9rpSl`uK$!lp{@mZ~oV{}C&;2pXpSNq4 zk-sVad-U_WK=|B$%0GH>U$pgE`ja(SM+sb*{;>S7IKJ{Sk^icY{1;*Ue$prZ)Ze84 z&V8-^b6t8^{_f?V|FSIo@hjj4E=)h=Py79AKWgrmKle}7mqUN9Ey~D0QDWuKc9(X( z`X{+ASpQtC^{3Oy_NS=7O?3XmdCm1ul<&A=%P;FC);Equq}?XI&1-)8=jr~t2k?O> z@nUxUs@v@E(NFzJ>pE~H!<xc_9ecBDJe@K5m{jXbs`Uk-B>p+*HOY`dw>EEH%Gxy}8U)Ddz-u2c0f0O=4uKDBo zU)E5^0Hj~mNXM_;l78w9?M++1a-E-k>d*5V(EcWU`rqQe)1PqrOZ`u6_s9BA{R!(o z^(UhFPd&Q*tu}}H>1X+Eav|s+ib)wY?p6(>|NaSD?ye5@e=fex;#2c&xh6eB_4~53 zwdIR?F?p~J&-LDkeP0h9JkG0HA|FxQ4T{G|CuF`@@M?H_gB8wO7y>0`Q6vb|BIaNRbrjzLI@)e#v@f3@X z>HbX0ab^flIWN?FVR%PVqpePJS^G=A7WpSb^8dX(+Fu|2sRq*jX;kH)bn-plE1u&i z>QA-O&wdg4x=is5PrkzRf3fG1n9#pAME?2BZ4^>6JLf&Q^gGW46ami_`&2-;HrG{(+4DG0_D~PUcViIsWOi=Bm zPwe-blwT)QejlCz{duD>Lw`!?XTO3gu#*A7Jq*KvmiSiY$ zC!;Gge5hT%@3ib_=)O&x}WO>E!PMmd7s=ui=U@>CuCo_Vd=4c{o(hoDdV9(Zxv?fHys*A ze}+ySkLc^nP;I{)o&TZ!q_$GNLi1X+)fS(+%=MC>{#X~vi6*?&>E+)1&nDHM3hlr3 zY>M)`dqIZ&R0HY1S^FDKw_y8Qp#4D7Q>yRZUGs(Y66w5N=aW;bY&wpHC*SHchj;(% zBFMidf5u_prIe`t;G zW^g;V4Ky)?Cm*E$`z~j<75$I*u=z#$dz=b7#_Qxb0=u1Z>yPj!Chq+k^T;t;&xhvE z*N5)?~mIIIi*HHcr=}J(|<^4{=ZeI zpU>!X+NgX&^~^KW{|-Stxqf6Yd_)r>e@r+1^xoe5bA{4R`TzO?>YvdCW|GpA%0vI8 z5dBB8-x!*wJ&p6p+ApH~?^V92|0PP;gCTsl|5>KtHjZ2M50U?7rJw8nZ+Rc`-=o01 z@s^cCqDyZ1zwwq$i28GZ(iuA(1;J+$588Cxom~%0`cIvza&BeAQ%^Yla*wh75J%Hu zJawA!w+?s%<+nw+{O)!mLL6RVjvZ*%d^6t~LqIWnH{^G<(w@Qj;rE1ZwDi={qSU zbdsU+PX3tg=A&OpwJIp+R32)-s;i)vKKk7re~-z!r*>1X`H*ef}|U#A{IJAOa`UvCoIDjKi#ivxSI%|Y457vCt^%9Qam#JN&_Or#2J^l12 zRez2;7V{5lv-BtWHBA2b=>PO|m49Qqu7mVnc!}zd@37Ti9{nwq&SXgbUmj}Z&-|iZyrA+Y{jSpQgzWxDjWI%hh3=!| z{>`77L;vfWWcF{G{+9j%jZa(lZ~Esy-_q}HEO#2*oV3KsnRx0s&p}GMT}&x}=cpt{ z!VS0?FOq#Q$x(0vVwzx7?W5AG?)1yQSod$HA4dP@ge?6jrJwyn+CsmWxV6muwwBJ= zC{5oPbgOW-3iUF$*zZCezE-0o_+5(f%5C9{G*Q` z+Gou&^t;gt8~h$UFgN{gOt1-&o|m-Uak>Wk$@PUw&rFLi)%V|^^ryA|M!IfS zyrcaemTUSm{-W0|953{*Qu;am{bXN^Uk*p%QIpC;|MhxK8R-w7hrswrO`rP1_y2?R zYrbH*&@`)i9xyLzxq+D$)x`` zEsu$-gXOn)hNUN|<44jnmGcJut^PYo-{OTfe8RPO%HIvqfAQ4gpYzv0r2q9U=zqqW zWaxJXH;n%6wBO|@zxq1USo;GUk5hghQ2$j=xYE4-5#?9$<(g2b+AQaL|1_?`Pe1$5 z?<~al>&Su({boqR=;!zA>W|V%y1v))l2$zD6`2pTqkG(0 zE%a|z{fw*r)S>=NyjEl!omWZaq2Iwe4>8mqerHd-X7k`kBmQ+ z`mcD>*<=UJ7sWdo;b+QU@e#!{9@E|9om=je`K$2!&zME1KVt<9X58&^(|_wlHVEnG z{CTV}IDXlQ=MN~Jl4)6F>352Pkp6lkz-J%RA>ksUO3%9f_&_yGlp3HrxrdZ!?ZJf4)}rXDz=! zUWN$ked~W3NdLXftUP0<1?9h5(@)-H@d>48Kh2j|p_X^0Yu+N8j?wu~((7nEmWSiC)ONe|u*eJ*s_05LLem`Ax)W?DKpGMbT{n{1lpL-XZv{5>S=cZrw^@}0>neWaO zHoWJFzoQPb^dv*{&$&(I6IxgQLv#CH?kl0Z$p`Izr@pe(Uw%n{)zA2D>7zgOe@uVJ zwJNtk)+YV)R3NnXoACTse)>D={poW4K=OO$GIVK&-13)m3B*wTkExu^F|MF9X0pyp zxQC4OaHZ5u68!}Mb)zh9PQ&L41h z%uTy5Yd z)9hv`z-Lbd`x}Y-DUBd7fLC2 zWmTRb|0_2A^sJwL_TTP13+>m>O)v(wa!BQ&-wo-{)2b&aEsy$I({h_H_NPklMX~a{{H-_ZzD!rt?T;1Ky8~v@H{;8_}=R`1mT+k##zuEck(LXk% zC#3&VZKp^-<=>-mkYCC>O#hU7AM(EcDm#YsUk&=*Q?vA^{vW0P>>rk1luQ0^Lw^!8 z3o`Og?DGGH{HIdTYOk2=-YxVOm)ZQ{_v-_fq5RgDn4|Ul8F$y*^dB9n|1;~XIt0g$ zBeh;K_uKlXt-k+D)gRU~m5P5z`_CNj#}rRno#x@2XM7{yf9viK(m(oa&_AWbRBHWc zcFRqFWvKq-`45SXe@s91KTLmA2@BItz8v+ZJ%8<*K>N$}^Zjrxg*mZ6L*rXg9{RuC zY=gu#>0W>S^WOJs zMH_y9nZ6wQzp5$GCZYGb6)J}Ip0`;9>F53hcScbD2WdUY`4h%(7Q&OSqjmjBYL#t= z$yeC^XPYh}|1Hj+Ni0W$cS`pB7T2}DZa?;M)T&Vhfk|Lz=` zB+}zKG>*!T_A5*0zwBTC!}Xt!z5$&%w~3h$wfz$_GPnLuh}wpo^#7#oRPy36`X+0A zU2gS+<&<*%uASm_UXJoDVENT_s4t|OiQnA)dGGv(`u;W@)<5G~qyKhRfoXQT#i#Pn z-|Tjq66t@L^yoN+?^&kwgy|pIKFAm8UpLF*+5aT{<(eSvPQTpS!e9SKbpAokZ;Aa{ zY)1ZU<)0XpTmEt`iWt)0Q|AfO>#g4E>&yu{Pv!3HdMMJv{&wP^;5=u6@|9M6L=&o2 zJE`^+r+0qbPyc4^KTP-(_2=2y`o|sJF#4Y!s&YFpSpSr9U9`%9`t$Z3HayRXah0y< zlNRsj`74wU`CvWtOXt13{(nr*=i&PE2;}cHZkmz58PhQO+wG+EhvwhH@@M@sSI_@R zhUVqY2;m*&t3qw+PrK7TTkMyAO6NZ>-XHy+)KOY{TSF{ZDQP>i@fhx)e|_T3AJ!=SY=5^MgZ{sd zezRx8=r84bg5H~}uQN+jpJV%2`BQ&ZoTKHmr{$M)W!ACuviMFRx*2Z%++8me{aGKP zzbV$0yB9Xep8uotGe>C4xjO#u*B7fk^ScA-KScXQiLR%a^PEGz;QOkFxl52>-Itd5JfzzA+v0P5YZky#nJe(!ZHqrJoA@mAVg<^AFMIn;G{~ z-~N|8^!L>69qIqy-v8n#-LwxrbN35!^*tImVz1ot57Ym@z5l4OGhz0O zs@c{*{ecGgnbEZ|zJYe#Pb0^Q7G*_|{iCD+XJ#Iv=lP1oZHgnFv zp+g5&#pev1IdR71U8`*Lqi1E~A3a+WIC`d+!ocHaSIuO=yzlAdNU#yT8j@hujA?Tw z&xp@Cr5E+CO!MJ|PoCJ`um7(0KZ{xx7`I6i)Av%Vhi0GL^6z`8O^EvQ_GlYEIX2jT z8>{{5RLu1vu>Kgx`NngD^?yR=uQ)H{*w>ga{qCAsvvbiu0`#YR^r!w?`gi!hp}*-5 z6ZgtRf0f9;X;%J;eg8Z8r-%RF&_DQ<`)|rc|4Na+kA8Q;e@B1pe@Xu4u|@+>Bqz^0 zZsMfLIKgg@X)})S)kA{HNJzq3~I37uY{)`~JEI{rSW7 z^&bq!`Oq&lF;~seb|z}`r?I9*TkbtB0IpK;H?h1f5B5*bSG*gydRMC9kKlMh$J4BL zE`AIBq}JJeQ05EkcFId}_XcBYB0h_J(O&mx`@s5Xen0*l`-0u=n`F)pO6`}MKRLgq z06+4_^(*1?Q=U`)677Tip{v$f`MG+o3hOoAZ{)7QeYdOo8r~-vdk}8m@)^dbJ*fR* zV{)Aj`D@Ik;QrJ0r-hGV@IlG({Z2(6BXAM+dB}a1>D~Hx@bg^TPvt(dPS*t8Z716B z^==;{(DWqZt;2N2AU(icZzFt%Z2rBz4dyHS`I(17(ntHXk6!fFFNe?XG}yoH)HF38 zYyT*5fXzSK+s&TeDfd#!y))FS?Wv!-?we$AohkX{I#as2&XjJR<3qQc$1C@3(#>@L z%szaPnb)dOqfU{~{-`12FuwBf7dx~{hWFqP&% zn9kJhS$^tfmYMtU67mrLA0UL1U`@E5K>PsI5LvCFgPPoyeGc}e~me^ISJd2SQw$lR~_CFuP}naSL*33ttX zWtsam>%Qu5;`fwg?$>m`fg9mj9&6!Fz@5)9-)O(vqPk_S@@LlA6r7HhuRl5`LC#9> zpOYYGCHT)tkh2o}=OoBk3I1~u8aKOKO2a@|k1^oAi${nKq`$Odp(T+W!kLTI@L*)+9uTg;I zzD>D5RN!p%2Y7#|+#wo4`m*m_?hh5jO|&ldbQBJ~Pd_-3N+|!26gU;kf|f|J{M=e>eOA7n~N1zwE}KyP^EOzoGo> zT>Py=`eIT?*>QmE!<9T|y;R)GzArJb*a)-&y+S8V4fsi)p6yQ!`vK+YszPtgp{zT+ z@dK;h@m(Mvfcub*UxN*-0`IH5oI%C7ceU8u4|xa9EBC0|@{f8+`>WB1f0>KF9ye*+ zn{EDC{<7`+PT*JMZw^69fZkhMcin--Mxf;|@HZ6umJTcS07t@m;Zp|{8-W3XEWJ)8 z=m*Ghf1y7HKY=wk4`0$x918gXwmKd1!TpKu!H@+YpP>(JbNQx0Xdi|%Jm+VZoPqM* z613_3BG*~wyWcS}*qA2xF@N6#>vCUX>|E^00=x^i-0xUZ-Nl0h8e;Dps|&iA6hPWP zx$m*=>@FUl>*Cc%=DaROK-xbqy>pNrAfF*$wBPT#eCu5KcaP3XrCZzjQ}#&}8{7|~ z3EzNjNUR+Fv(>n-5y1N(6Kin)BY^dN4W0#fA7t#+&PIUuDJC95c);4;;4jC0kZFV! z$akM&m{0OW``C7$HOs|cO8HB+`D6Z~*LF4nM;;+R*D;e$sv-y|3e`r?-$mx;2KkNIGp-&5oO#QbVJ=krDEl;d( z2D}3OY>};Rigva2Ni5wF{ppV8P;@-~^~aQ3kbb9Zcv&{fw8ReQ8{Nz|+GlQUaq+83q#W$zF99r91^S!7a|w)9||Dl7l`OIjL%nk(Sm2!7!&(EKdxj^SBgh!o_O zg{Ed1!U9^mHofSQmPVkXhG(9AuHnscq$iNakT2R7y|UoKT>Smx6PrM4Pn&--5AM%D zsUK6OiHVNu;KAlzsNaU;KCtl}Jh;0B@}m{*r|I3n1Gzsg0k`SX!Gp*lh<{xn&QR&# zf$5F#J0QRJMtDH#pKvD;eqV$K48uCK*jYs;Hlc$DdDsJu*3dp}*GHS=;xF;I4d*^+ z^Y4AEv-OW}yB&=8qdZ0b0sa6pZh(6(^y|(J9#Fqh_kvac@3)H}tia_UUG9sHKhVL0 zn=u}f`?1}-J9v%?Nyp_TK$Zk%=_#=D~HycHut)KY<&XgCNM?#k0UC$X^Lh{revJDDK+U z-8}dL{t}-41z2w=?$lP@Jjlb|7Wuc`hWoC_m47vwf6hZT|LXJPUwW7JMu7QOKdikG zVE(z=x5qqc`%L~N5mtctSGs$9Bf$KNl(#nm%)fes6=43QMz+U&q3!drw*`M)HhnfV z7k{!oQ%q9VmC8EIT>6=Os*4dIfAPn<7=a9bcy7tKlcfQ2avy7 zgcT6}oIP=F=URjZSBm zP@b-7=fU%Er)L%7405;u>Ag|D&>qCDYv+Mft8Qo17fHAQOOFQq*k_-{vp|yW>){66 ziSWq_Q2(rG=RrgHE4gubG8ccPxGztPySL@@A=!~Uz!Hn->!(yvLp5q zgqQY^xO9;%pLBl$f73Br-nKp`7=P_xD<6jUh|BTE{v++j{#pMN`qx{m|2+Q^)GzaV z>pzdTAz&hM+d?`U5oz9fAI6{sZ|BM|-{)_xS-fA%9~BVLtRiga-_9@I7D$w3ju5Ja}K8oM04~?|ukJ_m-vFOr{FuZ&GATR@Wa^vmhZ`5b`o@KyKt4mhXwOgmI3X8*t4G`R zFS)zTKUqiW&%a(*LcU{hf7qwE{|+Gc?U}D|zwXDl?+w8F>dc2jjDU>qlG_%V@dszcN0p91sNbaQ)!~#sMt=mcMj&_ye52(9dzG4-r;?_kY&lS%CbN zBCbHJKf;g4coTjC`3(7@J!PMhFU!SW`7oP*PL-`!WF3{if88BTtkj=o6m$h3 ztwVS~K0_bc+uS|zUjO|3TAiO4{@j@*ne}4{<&!lRZQd%X76L{kMeoSa8n9kc}&j7cn?5* ztz7_rKt4nH(7x@(W3MT|_Xmcr)%~BVnn9s_CUTpBXd8!-x6fd|jBo^jH)?80L%T$njUMINI$Fcxz#RKh3|(jtlZv{}=h= zc~4>flFzyxCFFzom+oxaTk^sDbM!oM=1UINJQU;6KK0E{e)aQ5{oJ=jP(SNt|F!%} z54QA%`AcuI?P@sxoFO(m`6hqKf7|d*KE@PJzG%0feNaC?e+fPR^Z04O{HvYw*Z6a% z*m4u*kL5MYAJ4G}^B2?oJ%7d@^OyD^b+a$^^A}b9JalT1znZy!jlX>Q$?`-#!um-* zn19qy=1ab8bk(C=?bi!?UgYO5xnI=tdHjW-e#TGwpXJZdb?jmOSid+*ANeDm`J&!i zSgvAd&**=4)XyK!M_qdXPXxCSq9^C(FW-9h6S;2sUpAh;*6fgHpY#Yl=auXFnNJf# z>-ztUKgOf|*%SAE72r?LFB!O1F#jU+a`X3R?Vmd^Px;gR6{X5J^)q>iZ8zE~p7|Gh zJ=iW%USXU3(;hWq#eIJM*#6D#ALOs}l)uJb`d8aOFIGa>zMEN=?=AS#?^V=W^5=H7 z?I`ml%op7?`Vd#U=!E#Se*QM^6!m;sh3e1xzsO%=w(F%A<}aoBM?RQ;?ol?M950qJ z%UnjpN$n61sD5}i0_Y8md6pEh$!c=fvk$Q612$y27y#tMG%53Q^Z z#OS}}?eE60T_l^&%-FF??$2Gu^U3x8yQ^bZKexem|7pVJPk~0HE$gle4Zd3`KHvS( z|1PlO_Vk;!pD&+(#FS&?EcZtzm*Ku*KsDSlZxdhM*Ej%vmyKW9*CgQ%-=}L+$^Mt? zW0~4LxB2WdnSV$rf83vX*PfVZcp-Z~ZCd%mw4`Tf3xAF9i!1(a?MG3+6m3@P{%qn| zFY?|q;vGg%deYjiMHEkd>nm}8>iRwmov}OW6YUd!9lOjwf5iO_H@u7as|`&u>&H_I zbLXF|A1}nu)pYZnUwn}6AC5J%<2UlR7rGtJDttHT+{Q!!^4%i272I9m25?<+x+~lO zu0uB6;qHcyz9GYOiT07d~I{v?9!Rf7zgu!qZvNzcH_1y^6XZSeZrXb;6y3 zJF~9y4%}t6k8fQk+$p#->pJg5dI*2ccHj@+d7Znk?h|gV^Q?ip2JZ_S<{#}HR$tW; z`nkpTp$C$Uu--fSeJFO8%@3|jpv~_?34IagtvO%4z8&;!LgUPQ^_1Ui{V`u{&Rfeq zR5!~C&cA--n2hrRt9n{<6Q&)-;q{l9Lz z0tUlb5SBl-dbCTl8&j%E76+m5hw zCF@)dMLoJl_uI!_w)lkSi><%9kJ{bZ_HQ;RezERXBfeDmQSZ#XfK0#BDdS!g{v73x z`M07Pbz;XXf9VGD_rI;5FZQJs%)a?UL&h>q$6yNW~BvW==Ec^IM;HLiU z@C^7rHCu1@+1u9J$&XqYfvr#h$@d)Amb3X9p7N!>h4-_RWmU|3RXG11>blj6T=^&biE))Ln}6B;eX`ym ziTq1_(#iv_2T7Hd8iB392+H>i2 zgy5bzN$=xg`B1dkJ9OR*^RJnPclz1-s7xd9oN1=xD*kcR56%-t`2w}c7sG!(_@XyO zKWjVM`mI{|GclZh@i=?Y;Pt}X{M{YmkLQMyF24SK)kBU?$zP3@i?r^qbu_|qZ6BBp z`Myo-TjJT?@jb3S5P$w#Gky~O=)YFmhyGWi{GHR(+^zMrb5XmlfIwpPjuAL!?mMVbB4={)?&_)iS^Yp&%ZH7v*<>s6eNS+?=cZ6aK`w%9rp_VzTQ4pF7Epb7Fma8v#E(Len>Bue|l~eR`WZ z!0G4P@Gim%T(0q>coyKigU~PV>O>Rh|D@F&S=84E7v!}7-TSf4WgUfn!d z_7`=cKkLsoG0;eypXJY8(lGvbPoNvR|1DF$XgxxDSs#V#N#2)2Jo)&Adamg(zeqpB zG5xcu@4ed3AJ0b{yb$w)Zx?6EpR@GumA`QP(n1Lemp|5f)c1I@uSs8*Eq`^__cisv z<U=lbtt=^lD=g zz;WI%+PjTO0!PF6Ouzjxy}bL^%e&e9i)#M$Ng~E8g_-%m)aAMJPv!^3F#irMwduIy ztbDC6>Q2RTeMhl+@SZ}pm!ymJ6z}D2tKnmsf5}^H|AqO@_b^@B50#D@m#cj^cAbsk zEVFzGAO7*}lUQOYk7AoPar%XajbRA7)lKJE8~OOb&oHMi+*m>{NwzY@aJAplF7e> z^2vEK+Tr}$wzbVab0-M)ne9V(%Jr zZ9bRz>*NFa9{s-3bUfO>^;!LlEpM29E{xc8Qe%Vt&)EKoA8y+}@;Ah__(a;`>$Kyx zlj2ijY&vC1_xYNBtgWRd5#opG(%w9+UC#c?Ro_|xBxl?A$~h7xCOdvV1Kma0A6t8Q ze-GIIh+NU%2pqiv`h)#`R(pQ`ef9o9ef>QWbUWRH?gHQhD3ZpSRO{bfa)TR2{AYx9Hp_Bg}0 zv-(`75&qlrMEIU!eV{#I(oI#l>X%y~e=+HPu4mi)%YKL8c`o&1%ZvcO`y|Jd8G+ld zemYTw{SSMTd5}QQ219Q*`&qP|Jb#r_P$71)T^q~Tu!KY@2DGWjMPxuj#>V)`%^ZiA>WHJe^!O>K7gH0g&X^yYG&fQ4&Xd|XG-EbQu=s& zM*{G>QyrcK$bT)K1#ZQ6B56+|({Nq}!07=u;-`?7Kn&^o%iqNa@1I{Ke^Gs?Q2VeS z?(xoVD|c)?=4ksq;eE(|gH~n;o4hu6`I`hei>bC2_9EDH!u_-Gyc64j(y77lj;1sC zz#tygk!QFrkLkOLr&X>G4FAB2#}ZP%tW~<|_4~@c7ehYlnwba2+6jonb-DR_U~I5{ zc|!Y>d_UjY+E{vxp65XRn(28yi65+ieMg*eroaB& ztogU=_u%inLeoh5xv^w!{u-(3Gyk}cAZ zOtbvCIuA%bIx0S<^EIURWZt8x;|=D==^?z!7pTp2i5^{b^h~+m(5+Q{*Z#}nlhA+p zu+YrlIAuj{{$_;utL+2pOXBm4CSAkuarEpB*R}!+hq>7X#c;Kf2&lW%hbL;_WQs6?O&PRU(se) zv=5t_m|D$OcUA8EtF5&OQ9s|*_9^kt;P~ik)k9bDl=BQdZ#(sGTTil_U8wa-DrU!1 z^OW986z?j&M)9<$_q4AX(Q!(y{`K$BPtUw;`PzD|%)ey!*TzAh^mpr$rAA;aUgWv2 zgB#HHc<49ID`1~k&WlQaiv6*dmwHh95K=!4{qNgO-`#9K zmizTVgwCOXvqxdF^ZTRFPRu9S*KV=__=lpn7@w6iP;HbU*w#Ui&1fA$#qu-C7 z*$4C2pEb$wmu?_`UA4c%c8=-xXlwZ(e+BBndC_!#3>aakSC6P3Cf3<;J#*;NmxJ=4 zoHV)2{zgX;YgbwZ6vuOY?(x=0{}@`(CGutw$VN$@0ysUuiE<9 zsm-mQOJ8l|rBuxq^wIJZvEhlHGsEKf{g!(5ALWmDS0f}eyz{heA1V~@KC0;}KBjnD z#ri<|p;xE9ol8FtKGX(uM_c{8^`fAD4no!@8{<3{tUngmZxPDq&4nh4c}aneGXDkn zr)8cJFm?y)j$xgkKzfFC*Wp>9da89tp;!W~Bz@?69i9a~NBA_-cROKR12`H(8}T=t zL8pKkmS4I*C^>9r>3^nF?i@cn`4d9CQDkntXeTdd`tIEMCwz;kwwC#EdmBDhX!~1= zHp?`9wu>BjB|~_Yh}~P*bl6X4e(<{)@f>gPJ+3~mzCGgBf%f}*Uz;B9Kd*Zfayq7f z(M;-|-25G*-zmsnzVbJ12c=ib8Ra}n@vK)JedC_m?y@~8Q~XqIr`b;^&roO|D%D+~ z_T`J;IK*H6YP9^B6!hiiLUZ3@rR(0@{N0Cnb1~%aNu`VPNY>Yw@ONvdoM)P}3-V1q zGWUcPSv?>hXQ@1Ro~os$c34wE@%ruza0dDc>Km@?Bg$8Jiz$*ixma=i^A>aFN*Mf{#^L``E#2NFn<0F-j|O*@sq+}lmjTQ<4Ecrg+3N8u&}~QSuEDcFE1aJv{+meV^Gsu@;YNVxna1%f!1K0ZkHR0o?@guf6QHTk2UqRiPkHV*DSwsyY&l=1 z-#06-K>c}g6Vqm;#hcZ+%U_$7Hlb=wkKYH9oos(u(PmS%O((oQ($V#tiKAUFsG||` zt%E#bN1G1URoGY>309dt(jNYg64zgURw{pTK5P1z60`P2n_fB(e{ycC81gq$-_QEn z`Wh3_dXjkC>oI1k%9Z#6jnJF(yU*MHOR?g2)O6f=!SM#)!}fvo=yAtvSL)|4uJ`vj zapd34P0jv#|9EOm?)=;T44X3fdt2)_j>q}l_d|Hr1Ba2`A1%GCCntvRt|oNc;VN&f zZ^;MWGmj5y4~kY#5dKQbY&`Z~!TwA5es#x- zWlZ>f@|fbo_mfXm{M3*hAR7W^eZXbRF~zsH@YaVs9^|hE{KbwdG4;w{LeDYfh>&)D z9{!v>_pc}JvwXWsN0`5q;=}x{Rs0KkS~{JW>tX5?Ppd*7%GEyY%#HW>^^^DWw%Ua9 z*T2~Oc)Cq5_Rrk;_ao-D#8g|$@E*P7FTwg{Ox&hJy)D)79aV3fLOah=ruY+858cy) zdb{K8n$FIy7m|Evw9NcXu_6|&pS31e`(E_A4d4v1?L$pvaQyI8Q|K=yU+SxHUN_*e zF=#I?#CR><(F5Lp6FIS?5y+gci04|s>Un72v3?>l3;uwskX~#U+7CPnu>N;ucQgV# zPcgZBMF43ZUhY-1&Fx8^Q4( z``6}0H}D>QKXOjB`DB!i3s{~MU!eF|MK+%}PAF9TGX37maY(V^`7+iweEUagVbD-P|c2$ya)IcpW75Gu+NO8tZqhZ#k}r zDE?BFN31S5FRI>#@oC>V;4({1SqV} zOgh5m2irMn#({SP@v=ry%#31-clDlL%HyL7i%&JNdXUvj8?Ve-nSFfSUL5P)|Eg%i z^`FP%{MF2%STCR8s*{p{{bZCvO#<@7K=?Eq{rp5a2AEHxe}Q zcy9ih-EI?N{;|HG+^w(7mjva`{JU?K#ZzyYe^o6lp6v}-a8CpNFeIrX3@#JrZ5T5)s3#CK; zT88ir8Bv?*(yrdlEYH>cUGb@n;Iy*spPWDIAKzA=i1rHSMk9Un20iM5`fSM=>+UAQ>`p5jaUk@F$X^O!1;}45`~=9K zi?{;hFM?+Q@)!9QZUFgn@hm{IOdo30zHrUG&HUvrvQyOac?9)~drr3enWuA?Kgm}y z)!x4`@3DQ71%f^^PV33^NQI3y>y?#H6DW2tQ%5OnFNbmd* z9>pSH;_8a2-R+tK0`+IHe&2llPK^J~&E{X~nTF-xN7^nsEiE5>IrBCBrfpA4)r0vO zd&lC*2lLf=*Wz6be*oLd4HjR?7pT2j?Nw?&u%_Y&zkXJze!lfP>QC@D@@H-T);5g4 z?mJnzyNiSToi|?dL-`|JXJ|W4{wV){vmICds+Hag+3qWUNyXDzZHtSc-EqKe=lS`I zD}QNeA6{>s;V3^~ZTLIDX){<>?Yzuk!t)G+&A5_+hUQzL_#`la@dBgD4O3SE&ybYG3f= z+{^s=w@l05h0~BP7i9C#ywEWI4ph4Me!lle#j{^Z{ucDI^;fw6*+KV%q_tcyU!2*R zuc}WjU!XSArTtr%tB%jre|ba)*v=fAg6N^Ye-`b5{!_Fu?y4B-!KZhijouIYHL8bt zApH?{Fz%~3d#DGTuP8l#s1acQBzD$N+{-l7+>ZX6#7~_!)B`z>KZ*E}bB22G8pM!p zi&77Nfltnb8~YJTFF<+#nyLCg`;w!#3*7I{{W(*AX=YMO3o`fHre4gQe?6`EGv)99 z@BKNmU{anWNi@N@LeLv{u?M0dWamkk~f9NrLhPLdFlluYKZgHHK)^*{0 z565|7{dBupeGBX7quOpbzuI=7wI;-M8ntVh7|w zUxD$_7U!R(*Fe8;$*1h!XoerlaVIb)_tK>x*!dKYpoa~t?kNoE)nLn%UW#!26yR08fttv5FueJD? zt|Mf=(Ux_E{`{-f?cWthJxf&PBCa=e&q#9SxsV-1A?Z|!h{(4vOJFl|zCSJ4cnW9al z?#oYHX4@_55!VUT5AA8bkEfJL5_D6ZP;FOA2{VhImFK!ZlXsFGbm=p8Cq!S;f}*C_r53KPMnW> zJ&qif;lK0(xZ%Hbb1RPo+%DW^L!$vxss_)VIx#+ZFwRw-J86!yz?n2LJ~KY6YEISQ zs##NpOsN{;A%>hZbnvXnv#X|`H2GL(($tAF9Y#29=Rb^c+-zss%sGD)X41sz)BiLO z-(68vb%HY>KaG>7Pn$e*ju+}QsDWo_e=wlwI{*2)3GH8h6gr>0_BES-Tz^CR&(2>@ zyb>&LJYSdh127-&)bRrE6F{-^49|ZyYiv4YioajO^S+CS;iL|Xze?R7urYML zb_ce~A@iyccf4-?OfVcf5aG!OHyBdu5n!G4+qCoj8B#8Gioi zbp7ssrUdzmuK#QNaeg+;AMuXTLH$hYyeZ{R{Y)HU^V`+%PZnSbW9(H@5Ml0RpsV84g_vHUUrBAOoMP;kt} z)BXJMew?}+*aNc>>i;5tW{}mZFn>IUe2e_s)`kz4Kj$H<2aeKD{@9NvKa>mYHLqV3 zxF3M^OHElY|LWfUYx$S2{v^F&{#f6K`J+CE`D6Yu-(2ON?l(sdywRV3Y#*X81^KIe z=dbaXcr17Oms*g!eIP!}pR4!qh52Ls!u+G2(Eeia4+Ahh+NtdG3XgNnzc_8tzkZ`Q zd;aGJ%O8gywB_8)ruZ>^u75F$Eng~5)4wJ9hZsl2(ycsbHV|%%ziZ(ZsDe0?a3?>& z`KW+a;znmR4LOFTup4-@gtOo8NtJH0a(AYUKAnC6qqj z8@9YwI`qSY>AZrL{B>}uxhvsG596SpM1bl z3SIrc%7MK~+H$^v+}}{G>2duL`$LM(>}wz%+3T6ix}+CJdv3>~&3pV#cIsFU6H$D4 z|5b(Jxxb_K#%9KOm$|E+K8rrI=N$9$k-74JLy4{7W4$fkwY4%&mObBk4#UtjM+{50l8h+9+>^#5#c$lRCyg9tBSYetkBfu|&X+id&~@qT}L zK9&QIXE&uK{HeX~Ps-ORoe}O-=H}3A;J-F{?>rU*|-3aV_h;`Qw z?QR4*Yk0F;cO!6UbF?ovp*`HUy9Zxh0)4}JxyV7?J^1&JHh%h$?ndBOb;l3wZUlbS z@aeC(W!c+@p)B2XK91 z?Vh-=9l&{|L`4_ei<`Y4-Wk`$2ylI&GahchzJqLgpTx5O*BeIm>S6>~-cxuM;Ce%M z?=HCGyh~=iVG7Rz`3(7{J$}Pa$L8Wc_OMOCJ=FCc&OEqLfd4lOAe$o0kHK4@8!Ch} z=8dCx7Ko#~IOyXiF#Hg>9P9YRU596Z+oXSk{c+vjbjJo43;Sb2S-$cpHSmc}bhdV_UI-0 z_`VHTaXI)MiSH(O7UV|1|sriq%~TKY`4ClsB;7 z1d!Q}g6B9OvmfPk_ybr!l)_ISpCRA0oeNv+pNs!DCfNEfvEYySPd^R%0cYL@envGm zW-Ze#`dk-)xifwJU$U~A%k^u}&5CD7n2wl^-@CfjyLWf9$HTq%eE0eP`+Pmmn%cE0 zbgj2mS65f9suHU2qTF=9d-jx~=hv@C`Q-9X{%iT2yK#Te!>C_*|EBH%{-8GoV0<5b z7r%uZsbBc35m(5}0zTM(F^Atm&uRJqzlA7&b_MDeMER@mTZr;!YY+~i{3(P9)pt>D zx@V95dPK4M@AsR{!D(UT?{!K1{U_~z&NR)xZ#Q&DJ3gpM-oFA}P`-=BmG&TLQ#j8d zjs8LIx`N;I03L%2@9zb`%h{vg&#i{vFT~&RSm@JPh5Oxx`?=>7{F^HO_51B$ipjs$ zwfY}aSov>wMdUB+4^H{b109VJ<@cZMXoM(V_TG+0i1KAtb~Hkir~1{7Mu_s%)OIvN zJDg?la~|wygeo_~_bBA6z8~>H*~e^n)pH$<&^YLmpMrf2@)oM^V!hM-`Rj2-_YVk= zzg)p!``(*=n#hm+AuZe7C(kSDC-|XjNbgDczQObSkFFu5+MvEhHNwJb^hH47dct;? zANvFS9ng)^9w0pZJNh>uPvxb~oZm98KXOi(hx7~|nYQ1%wcp@=pogH3(f+m;b0ddx z(>?cZKb%>t{vShmo13Jj4}x z0Qw_%RKJh>Ku@UOd5JLj`6W`3h`~>H~vq#>F&8x=`+RTKlm$~HalT>_(SI5 zqy1m8GswbztNL=-XMxtC{mBk!Z2U_Hh9q_d2L8HB28P7%LH`PbgJ{3yAzVo84DIC_mFse)`1@A&7jxp9sGL5E(A= z^0&B4S|ffWe0<{m4Zdj5XU4p_l?(dsBmWQm-)ysRc!Mt~?3h`TkF7lFf48)uKGyfo z)d0G=4pY9D?|a5~)PCby%7d$7?>E_i&VdiH@Ip=k5}6(#pSmksY6P+F3rDi=r3f^Zu)EZ259j zeplyrMx6)ysvdZ#^zt@rn*8<6@6>lrQJ;NnxhcP+_e-8%)J^%x^NYGGKY4yoPvs{a z%I|2vkiTtpNK}5FZ?qrgw}N*H=f`-zSozTn2`{=*|8l-jmGbL5-{{Hntemv-NB1tg z9_(lNocChAEK^)KKS{kP?~Ha2<6^nvaSjlO-*xE|dKq^?{pYUe?F{vTZp{HF4~fdZ zT=$1=v^mD(UMif=?ESiF_0RQ<{tMRbo%bC;d!xj>*Y9eN&iQA)caGz8r1mqEzp3t@ zV?MO+9Y=4FT*q6+8z)@WKi$usm>-@$laHqIPn?eWf44Bd=l!;6^5@rIjnA54IFT#4zY+Csk;>`5YW1Z;)AZZbmWS;E z%X`d%IDdbQUokn(KOl1N=uhHHUh@?d_sjawb$Z?++_~Lrq9m<~QY? z;41%)lr4w3#nz9q&2`FmhwT=!SpPSnK+%57pXj$l=RL0iEYa}J03te%F={|Od?3E7 zqv5$6ct@2h-R;ugKC zluNg~Ve?q~U#shy65M?<7>s}u3ZF0Aine0}0Uhv&l{X}o$-d<)V zz%O|soPk~EJa~`DmhQwnESez{&)NcF)AA7&kyh#Sow(vU7jD{Yle+fPN$)j zhxe(`eKDw?tbeXIBR{1HCg^-S`PBcPS}0yx&yL3bEy3SdavPC+y4QT;hv#2tHiqkS zS7N@{zq(m|e{8Q|arwDFwyzni(e)~3cD((&tGz9ER_#A|zcUY1e&V?()AbRIPh4J@ z7LQL{#^`$|(EhK(8zkHX>ZiN*s`c4u|EEIjWA|KVus<=i96HyixcuCI+1Gpz)%|q1 zKQeFM_ZVAl?*xkr%l+Ji_5*_ZEVEw9H9q@)u4XjiY#X2XPf-48Ep7hJLR(HgkGwx! z#&?x5_vkUxqVjtx|G=gAzJIE)|FqY5)8wyj|LKQ!x8?J-o+*C~*X1aG3oTa*-3OWS zJIZgH;Fl}^)Wz}qGs;goQQRfg*Q~hwvVX8#e)!**8pSc_7k~f#I7sDGJ$E&} zcUhco-;TyS@%Okq6&gQ(euVA=PCe(j&wO688b9d}M@IwhcUFEP^&e=td4A5bhr<5m zxTfa5I$I8}q`3U|)!B@E&5!#JdYe-c8^Hb_*X5RIeD>Qo&XH0+<;k6B-=mK5U*02* zOLyh3CGPrOEK`2cAzpMF9C*U9(e}?*`?j}aejoc22FCW|yynH_mwA7={P4dq!}WVE zsEF79#6z^6wS1I+?roOOdZYY}ztQ{lu`cBqd!YSJV*Iq;_nonppX+PmS13Q}Fdw@A z_MgE1`+H%nTUgme*uV8ekm#6c*R$5sTX|Gl`O?|8o#OgoD+TXfaHr}|;;^Xsud(&p3snBYsxW@uxQSVPv$lV&ip#%R z-jlfe@ISsq_f&n>{V0{4KYy3yqx|`O$n^Yj%KvQI<`ZapnbwS2>-P`!pYpT4AiqBO zIqt@CO_=>|$2JWccJ@}<`|`z<|v82Cs(7Ww#a zJHCl@aI?L^-@DO95L76K?(D_e#NPiY-G4&%&kCMsUfBOd_t9cK(B=M1l)szybFy#S z=aU8CJ}>T2H)K)Ir|Nf}8E?M}$~1x7$JTRJ`PBc41fTlfQ1`3IX?%MZ%`D~jWxYFp^L*ufoz`0!KTtKhGNpZZ=0e}Md`_t{E>!%w}hg+En}^rHWG-ZDo6A^55H zwc(4s$ z2&wd1ln3&ec0EGkJYD8utWN_SuH*84n;u4Jo8yrl@4?!(Jwke-ANrWc&%iH4dHiau z{{lTD^8yPRn;N7QdTo*&=cK%W`qPUsF9`Yyeujx#pZq)BS^Y?{`d{#@rkJ^V_`}Ly zIKMb2@?+e!YF3Ysf)&b!#H*|&VL5U_KkABb={)AA@O|Y%#nSK5YKlu=i~mMO+DY4a?`!8^HVn!lYi-l zR(^jwH~b;z07vCVRfPI$9>>B1&@X5oWW7=LiOoV{{nkCU8O~eSEN|bJLR=y0w}&tx z*0=NIW=3fLF~H{>*!w+&aL{4!`*UDF_4H;Tjhu&K&4vBcGsquw^SHQv3~X-8Q;mBG z@uJ*xr=5H6ImP6^|0SEk|HBP`SosV60Lqtnb~7VH{q^u$i2L4DKZpB+9+S@q$W#0L zW+6Qfe=r;NqAvmukk~(p{hE0NKS10kCwOTyBP8~aS=fhHA#b6}j0eD$Xg zS7;~HmtO^ZUIsoOUX+{e1&{ArbpP1bZnPE-DJWO2(kV% zJLCK}5bNLBrMnSg{d>D2e-P_GH4Nv;fmr`$x9&!W^&jB35bM9z?QVp4QEs}sx5yS< z-;jOOrf}!%7XEntI+4F%-@FY}D*8U|*CFnI7Wymi=bD$XZyRXfC_LYh{$DqI-%EI| z7W=$`b~*!@e_QC^eRNsezZ3!4<=4Aq;XhLSveqpFe|_uSSnqT{7Z&6maGzf9%#Zuo2YQ?El_P;ZQTiToaZ3)KyV-)&?J*4_#I0>2Nxk1!#F zaR7<$AWTTs_le)dZ=r@5FO&GSh$}?-Gp|7&5arL}w-Dub|Bmz^%5M-Bk^(@v>7IPb zw4&|bVZYi0xxf9j{Mnare~{F_tY=KuqJBYA|Kd0JEhP0X>l?H9EhP0X{%ZUdlKK~a zE%FkQ`gexI&Jkfkl;3{|_<<;Y4Sowz{%Yuo5arJxOo$idru*AZpD!v_|IIS`hP&yn zg`^c}y2YLPw* ze+IvWD8GYy2~qwuehX3l)Z@@c5aqAMZy{cko9@L!wwqK;{#y^T^7~ua@1Ildknf*{ z`S0Hk$D)0_t+6pb_P6vH^cDU(_=Rpk|47TK-Y-9C6wZhQ76w@=& z{?8XWp6|7BdCSmc|2Vk1qj5p}9;mveqY0kxknayyUE9&r2@m*5dzQTpzrnj?GvM8& ziOKpMaejHneE&Cqzxss^`Tl?KA015=bSL8b=i_<0zN5*2c(LB;?!M;7sm1DF#y{n9 z+FH5)Y8+I?LOHH4w~~0`eg|hcKZrXc}a_j`sl8#EtZ{auS(<`mtyp9PI&^qX0RNsHpa3A>#@xpDwi|*M^?L9HN ze^-UtKj&U($|Rv%Rc7^|nR4m=tNkn8J1l<0`(N#UXtuNcEPkJGe>v|fdw)mo&;8eO z{cQX)^atJE7p)x~_y z9pe6a+?USLa#DVOvBi;cQhw$`IVnH+uI59B@#tPXWtWPm{M^57?XIni^BjA-*h8aF zarqzm)qd~!dX5zD!*j}M??L`Fogd4!wBy&r<>2jYKEbV)Ph376tNGt%`P7RIY6s@D zijRXY{>;WVw_85(;{9v%2i!0WBR;ygBe{YT(a(v~}S*}0GKj^qrv#9)~J7>Ze!xH;TR`va_$)8hs zlJZlYl$MA3PrMc=pYl^qPx+Leawg?xKEB4MW2M6$GRk;k&nDgE{m*c-qYZf$`=tdR zl$cMqv-;%qv+}c5r2FZ1c7H2hGk9CqGjKf%^G(iwr8I!+TyjU*?`-P#fx7-7yKQ`4 zG-)rD)_hv&`U2Wfxym06b#j*DJ<=KLz)P`KT=~5JH%n@-h|16N+YTR!{_H0u`ThI7 z{#Jgr2z15%TlP2P{kYD@yNn!r!(H?__7=z6!>X-NzHQM@`MkHOnOJ7tZVUf5==Xln z+XUG%^C!As?wQyZ2!03tCNOMpw!=OLU-mY2i2pURlYUb072;1W%lCUTUn3m;Iq0Vb zt&ugJ|e?GU{I#pRcE-%2Bo`e{2a#{$-r~5l{{9`Aqf6Ja5S^t){@?%&cyy$ZM+d}0Jr2SjM{xiSt z(V@0|v%M>0^6v@dQ?GcABI-~GOOY(6aWLwC(> zW5-n%-OVoCb-C!rD#a(K>qGZA9`+GESz774S$z1`pey*uwaPj;SKI0t+b3n4_jMg0 z`NU`PwKhKOrYP_G&&K(Dp6l+8^QmX=Z(|EWJRQC9p}S~&wKFSYf$DMD-q3yKkh_nF zwjXO0pE0r@t}O2Y#KgXKbU*Q>IsX{K<)dvKQ{S0>}SjG?qu6f;`6=s1D!3b zJpl39RpS$9;&c0vHlM7N|2n9i{C4MJycu*TZpbikCnfwZqn(U~dl=8nomj}P3E<9R zej;~7_mHssGrV7J9{iwV>TG)dtnNl=isql5*xd--wqHDdg>NILTse-qyR|!XFIj&R z==w9Rzs=r@@(nDkzcPc0>krqT@%cJW>piP>gDk*B2|nv*AL>cg>KpZBTea8As{I}H zCDx^t>5X?z@cG=ZTuFBg8#)bmZ{9t!|8VE(`>lSmea@VL3R+P(f5sWS zY4~Im=b*p+p5Xm=JV5hTd6#K9R#nFNzVfr05BvAniY~mKOvJBI{skw-^I4_*+C==E z@>#B=OI*pnp~YQqOZ!u;=ll_$>_zA|jAkzlzjw0u@V!M>=JjR$+Ct56_L;VwbUNDh zg!c)aQGUXH`{b`J-<=fKpOF_>KIadpKSw6`j^2Rw0^Xy2O;yBRz{&N+Ic9zHZU1QpeL;K8 zU->eZDc4iF&wxMQ>SMBhY-1JvQyNcIDhI8d*WIKb>WyplMySAG%)>Op=zpVsdd8oxe=^+305?5zEZ^?w!|XmfD7 zjR=3pI?`4V`>OjtLZA97*hlPG5|Zo}>+jIS)L~z9k{aez5`81y?yG zAvL(k`b{5{1oYM^xXnQ9uimpHq;S8aff%pdsU#%XKh}K=_8Q%BKhO$!t`R@iuOy^T zkzV>^W+?IlO{k9dcdnjh!=0T`9#DN3pUVo3@Zjy8!uJ>Bm+Jb#;_ zu;Th7b}4f4`AV*Xu%X7xZea8Ablq5~25`L=^{PzyTxUi;^*%ZNP_FTt>-tW{r+i0O z+xwEAVS(y#i5%U!+s58eOn;v3Y~M@XT_eMsWdH3}c#o$vU!F_mjZGOL&c9^$PZ^bJTQ`)7mRfqsfVb7U%{ZBMm+_Mvy!(bj+6w3HF5uD0Rc!MGo& z4*C#mh4KCAsgNckf3g3mn~E^dr<#BDen<=25&27aYFsL$df>_LBkXE~sTbUGTlD?4 zUhS7_hGG52w>aNX+ilk^u0L}An_SeNv1-5LC%)gy^}E&oYIqo*ICDHV(Ds>n)j-!f zay^a{rZT1l*FkFj)Snt%2g-b?Kc|e>c3b0TG@tq$PXp+FcglGk1fO!XZ!)^RbJelH z`=OR*SN7+J7srR|TZvD7`Z z?>)UZ4y$$CjkpnqWcxNj^Z8QsD%;rVCvoUIBcA^7TW&|xHsc{ zhTlS^TcbY;dl(API>o~X#2B4+vlfXPm{Z^x#9Y2qw9&AX?nU`f6eyi z-?jca*v+<6JU^^V?>nzD&QB}}Dcm7=P@pOH5 zwT`de3;oGWF0?;Rh1Da}Xn4`(`fRq7!#djbFqd^Trge_caWJmW=5u;vf?uks-a=Pc z)@^6@>SU@bI_3AbuHL&7)_r!$?{A$O-N~dtrU%})gE0>?rW4Mi=#<~z+UyJeKArOW zTW9w}e&E%2xf(!xKipyHt%6Ur;!~T+gfV1)Zujc~KJH!?AL6_s5`EHWn6SuduzfC-e+xrQ=qc>Qj>&E%MqrN5g&7i)qTuGPD72WF_+>wetUn#Y3 zI1Tn~-Z9v>@K~!4*}aS7Q-yw#T*T+2MEPx~F`p**#OHo}?-K{&^X4J;zRnBwxgkE> zCn9&Q?dMQl&MT5%t~Va0_>=FlK=rs4`eJ7J=6C;XUKAgm@3t80zjH6OEUf3T-?ds&PH+JAO*9XaKlpzFMyX11N^sR;&XK1qEzMf(rr6Q9fz7EjJ=5T6g! z9xeCqh%jeujaaOW+yKwDT(#=9xwRy=KD1(JCJav<2WMJyb9t>D_w&aT$4B;smW%lO zQ}x7rXXA%~_{V;ox6+odOz+SA0fMFWJ8!x2ub>@C(_vv2HOepPt?irgS1F(RL)>!8 zf2YF6XML?vKFgJKslRjwe|7Lu=+8#$=fA!M`g1~I{d{g8D{n{5kS^EH6Q2(dAnWJ1 z9|8M=K8;PUH_>kHTUbxOtTW!@@N*q9?e2Wkhx89=cb8mOzo0krMLOEqIlT~nufn=| z+S7?0owHvfvlH%{h5ODONC)G!%#*epw5vKG64IRud{HteKzF%Sa3za`s<7Y=%KFg`W8#&9BL-(2^moJRsvq13~F|vU?6ovGeO~dDD zj(?7eKk2{E*7EN@BFwWybDr~s%A5EeznC~E-^RkyRJGH}jn$H!qZS2qGcZM5gCGYX*_#yizbzj4t7WCv|jPEsQiap(6 z9}C*h9sWU0Ocla}xX(=WH?XG#vA>e~wxbc^J~QdB5EsOKW^(u~B=*p9{z&Z?u+IhU z-WB#U@Vodev=Zf$^9wkB^c4L3E$)(=XZqN$ik{C^r}#K${xv?S&mkX(_;{ao zG(yCudOh#}5uX~w6(T-q$R$^8p_YQIN)f{2e>hx!B&pA5o;h)>lI&?gY_sl#s};*3wpPHXhUJ&tdenNRc#3zlgkj6v*ah^|R1MmV7 zpIZDDB0hCUD@1&}e?T8W#Had4=p%^u82lFccj3eLI@_NgOX|Y*xmNdwW&f};fIJ5_ z&)diO2io@8qE^@+OYGxHOfzFq#8d?PhvRL3#_08|jzc3(!?1^|d$+f# zI<3qs-W-0|vt{4wZ9MoZu0jS6U>@Xsgr8alTM(3MF8m*0|5NyRu0jBR7V){yLN>sD zsqk~3gaH0J#6KTnNzrzR<)dqRe+B!=x>Jwu6@7mhoj+~;Bg%1kvphbzgDgIcH6yx$ zPZRuADgXDQ^-c3p{QdrxYx(ZBZWxpGMEgmeQ|)Lz90%|{8^28Xv@gs)4LqLeowqMc zt$}?Yc(gAx&-FGr@MvF{dI5Mo-@7o*@gndCFL|+CtiS%l@7@9UY_z}3=X3FXINI(n z)7rGokBhNimpK&syI{W;_U~%p&kQQ8t8fp)8VQ7RT}2K2RS0MQ&O1D9(g#vN z8R-$8!J3c^!jl*4IjaGCHe1vtYJWU+P$mo+{$kTu(z*#&e~4?rwR%B&940zPzYBx) zwqM5hw7>T5x8I$tFWMiw%4a_@Z-1=w8;;(9_Q(8gC{sS|ugQ0n{|Ltkbl)2GD>XRp zu$*+qUb}i-vG)1nMVjOii;LKgwMI)rdEP=p<9X;m<8m*J;!`-E z@;T%mt}i{Km#G4kLvQ6=1?S9OA$^bYKLV`R%bwLMq*Kqc@tv~~4!S`6h#&0HBcwNx zpWx^1-Xo+JuhH<42nQ|U{gAc_#PQP1Ilv2aCGwN`zuc%EA=S6Pi@{moLYun3%bN8w z8yCap?Qs?#zsDXSPF8;k>${g?KNXq(tU04cNOO)y`JgX#r}YSFpKq<-J00?ZK77~K zm-iF=piv#sz9D{X&?BU_jck7Dvw9ezZy>Ma&pNK#7kFVlKd1#>pgVOx7ysQJ7|-uv zo|5ul{|v^z^4Yk4{Eqx2%zdSY5&A*Pllr^|`nNqo8RZ}KhVJAsXWm>4pJ^xAB<@7} zo|gAi>xg}Vymy@kv3~W#-XZb5Yu4eN0OET${c&$2#QidI_$`!w4}XI95Qyg)nNN`( z^wt4*o=(F)=lCsj#pdwiel?%LP9DVfc5XfL1AUD6#75lYc}KGv#lx?0<9;ixF;n%2 zZG~amzSXxaqVIQK+vhE|Yhe5j`4&gpqU%#Qu1c5ZfAhOIIiAV;{=9>2$2srGc8qr8 zT>s%{f+HGh{NrqYDXskKPVx7Ft9+gd?%r+N{R-u;8E4~j+$^hn&Y!t+TA1K>YRks@ z^V)f9t}LcM2H$gXd7s+$NA?MCZ3^}YL-2YF?F~+JOf{%aKj=TkZ`_ldkRCvNhtD7U zHK5#@(>MJ;qD^b4^+DXb<o<+0LkjL-W`*L_tCpGWaMAeVcz?dQonduyz) zWWCdVBRjAQ=B>IIEJZQiFKADOb_wb1eIQ>co`)T~ghcxeC*1|>F1wh+CfV=+;X)PC z{^EU=8q_7ESC+&767U()C8Q75*zl^sUC__zVsa1LaBt@>MyMa!6G>mY6YdYztHj5S$`ek z3S~5X8oz~3I|TV7eGb2cF8|f~tMOZi^M%1`%nyP%Us#9VLizc^hcI6V;`^@}VM4r! z3*Dhl-gRs-d|G3FUb&oKtp3ROPixa6uRqfNk@=|r-h~&fp}YUi4;0-nn}VEzvz`%Ovws{K0| zp_0R`KR5{Ui=eeyo|^HU(Escd$|(Pc3*El^wtK9Y{v1PmX1HNm8E0-Cu}@fzEIst+ z+-3tp%72&bi|-l`zss`tEyQv6D*P7WcbSLZLfrQ=jr@f8otA4lzzFd>&D{^*X&|n5 z&)~NZztgJmTZrr3bI41G-(fz&gzmW0;#GUb03*bUxX|7Cx9O$D@R9FRxxDeV{jvBI z|4x-}RpKGvsY1jLsQ1m-hzEz{&sq(EF z{Z7rww<+{R_5%*^TPXIOif>h3#D(sEyt4g!#qjyqSbTETw*A==`Ww|BX@9)?v0vTg zINuk<=PPqbJ0leC2b6(*Yc(kd)iko>Ys+J%Ip zgiSC>_6x-M!k|@<&p)z>ah76@CrF+@$uEt23CZ&pAiZ}f@&l#xemUeP^c&v4lE1-k zAwGXKNGrsPxX^v9-M0r9!{=wk$6Z`MJ}wr4RMoT#iTId1a4(SHVFu5`4^L*bgI2Nbm{sL)ri&`1q3kPNWA3 zJ`!GyxI%)Dd#-`V;*P>+b5JCM0~2&0xyu@ z69_(U03VRx;~s$dhIZ{kB0kwSQ9h91Bl!i$OGxm^0-rR}3JE@0;N!mseFX_VVSc!m z3le;?NbkReaFE~=h9j;E5_}|o4|xj_pIW39;zeBOPMm(tz+(8k{x_S#-wQX!^+OBk z{}k-Uh=;r*d`+W>2KH&`@iGpxPRnDHa_F*IT3i`d8`X}9aCCr zuF5vF1NS@I=au7A>lbSNdYsWb9 zG=Tec=LTE7a+P08dl(&8%_u*q57fhfx=xw<8xXhIy55}Q|J1{^nt$$6i?gTsGruzR zzyHHaX9+$T+Hb4`Cj+q?rQ{}D#d4m zo)7%)^JqUK`1nu_YdroA$|p`t|G@I#vgq@*Uh&D@i~hslg8t-=+cbPeP=AiF zIOuK7;mx%?`n(VyPtV&6l%LWBJ@k1rHMYGaZb_USjei^0L1=qU`F>ITWd76-PoFRM zU^mRc{>K&-njf8aM;{g1XQp)il`lXGqT<8#p}fzL2|n>TI>9GC zOI2RxLwqhw@EyIui@JW9@u^pDldpC%#I4Eoas8oQv0NP~OnAp38uO5k(V59mK! z(VWq+*R{n*Uyp{aj7v9%&()Z_vYqTjjtzM2LM@Lz7wGAS*J*gHWL&3a3C59!6!uR^ zFU5Q({6}0b?}@^=z^Vh#kBN+P{Rx4ierKz=f4`IB{jMjFo_#KH)2rh7FVu2*>b^Xo zEcSd===gKy0X$#Bi30MNS6qMIywdiwYc;>Lu7l3)ZQn=AHa`UxA8&_#>2K9qyY!KJ|G1PgeigKdIJKf2v&MFH`>6x{jNCU->N# z)aP`aeQz#Ten;h-UoD?<_ncnr^e zisQremBeR?u4hc1kGNq+Z3nd8OZ5KFoTm7shKHO|<vwV|7RQI{cZd(|cK*BP zCw_ROeXepf_IV)=4~?^W;B>d;aaE2rN5|t=D8JQ|I6tfW5sU11T5h?`pZb!-=Zkiw z*F^P)_`JCv@cE*EPjJ%zEIwIX-;`Yw$H!N``?-C7U6pqqF@(b9YkcCB)}{TcMAm5b+Wx+?E^ zl~x}ZpZKgx@QF{4EA)FPFlJig^PF?GOXbQx<8~XLd{_C4mCyW% zTkYdEKKFyoXndASy=KG<;}*#NNaebJGruo_$Du#p7549QPqX+m)P!_p|32wuyUOpc z?I-s&Qnu-n;FoBC13;qv*>Mm=9f;~Z(<_;1})cm zb*s<0ZCq4;vgH=Zo;cqvcLq^FpWQ32KYx5?Gpg16zSr+`GuO6@$~GUbR6W%1&{7T9 zpzFM{$GRcMQNFM1_5983bK@z0t**0RKY5w*e>z$7*L~`0l%IY=`)@DV=XH(pOFmQn ziRcbJH0qQT%SUG>RW%YjN@Hx;`uD-tXB}DZfnlmk!bUDnG4!Z;s_VuFbz(`ETuQ`8-e1Repuy zL>$`;{%Cd-pEZilZKi>7-fC$UuCqAj&fGM7cE3x@^HLn2Q(9~J_pyB9vrfwyoMxXR zN6UGD=95!USUO|EYFb{yXh`Gn)UiUur(TTK+2KFH5+W z9s6-EiqCq*=c2nXet1zqe}c1YKGYk!N&Q))<)=NE-p*{$dStszd>YTS0^DEhSG@<}2dzIMp5K`$kA!Dd zqkN!OFh3K$|6Ir$Ew940kwe_*et6--Hx=8TL(Xq6LfA)fbCi{C;rKPl&Z z)#B_Yp=lV0k@QtK>q&_AW!3mC#Pgo2hrtixc~4b1>q$uF8|6H_jEnQ6K=ob3jqXk7 z+}W*|{vG|B%^`Qd9^nr;=RB%^Pa!;Lhxy(|wg{>2G1O-T_RG3|i;%wC4E@vgjj@LV z{Gb7cLcVRA8h6bWA<6u;hx9JOgqClM`oA6a02gi%lD`_^^I&gv(-t9Jd6w0u?2;{D z|FDHQByH`5(kE_Kh82`vjuBmWSU$d3&wBVG9J>1^Omc8)-17{vvc|zq;W2VimoIPp!oT1J>m2>7yno@}v zv*ym4U3v8M%E@#4D|4tbV&2itp@+|#Hgj(IP-oQiNmJ%H>N%it&aCP4D`z_htDJrG zq{)@I&8TTJkJ@9w+{&4Arp=l;;)H!C&8RGkWjt!$^y$Lw?Zo-{MB`4Vs_g4j&YnGM zc5mD9)-M^?^`r(}+OtpvwUn_by|-!R@L-NQM>IVG-X(C+Z!bGGW#_A&c)?r-02 z2dwY-wZyE{{YkSI?jEM&ya-)cpD+8n<8ceGC0E$@gV)0Lx0G$JVSiM|@yWk0!DqYs zZ4;Xh`z0)IC7yd}r_5pfOm>HUrVR=r@9iA?b?|?S`I7+e(dxtdVbZ-{{<%+0><2sK zeg(T)_|rS~Gshvnz`=8RL_bqKuwQtNaCoka_z|AGCMbjMZf#H6E82f^wf{EYPqY_{ zbn*ac{EIApl#i}FxAIr3`3|~M<<@ad-v4dobNqyOyuO#l*YCitQv&VhamJ4>?Niok2DH=RK90;cIWIx{u#zi`5~zI; z@w-CjB{&{aqZ!@MSK}*wtCXL#XUQplqwyq_`$x5BVg4!Q_0;=wyovXDV4ljYddXrv zr#LabW@iSeeGmC~V#Di|M0~a*YZH77+l1H9L_U0?K1jru?zddEYH!5*mRNTFKmBx< zHF@C!Jimh&f$Nt)UWE7e!7Yrh@^aqOnLj*?>gsq}sx;G}6*yuCRBU~rH?Jr9X+87n z3;h#%8X>MP%q{3?gw_E#Sszz>e9w?V`$DWw%*{tQsI0TJA+Xm(Tp_M6bdi@3*B7SG z=xKyDb$wy_G{gthx4y6%c?t0<(;s|(j_AAH`O)@|?+;mD7#vctpYkuY`bRyWE4Vc` zP4oM9zVnUMGiUJ#-ZFGKKIC1Ejvg-7-{^hgdg+9r#yltMS@52awTmvO9QbklVm18H z^@~2%G)C7i)?rO!bUmV*SdUl@|6i>?46t7Dt4YQC%WOyJem-kd&nSL%YJZl#2l}^D z)4cszaGAxA&n;cSPp7Cz(DYL(VegC)-P3`E+X7U?}7EmE8^${FbZz72CfNFCfAt&CIvD zzlyV@IDWKGBYr$z!<}p2YrL;>l$DS3s^pvf zJ#)t{A*W3FBN<=$Qx%u<)gFm+rqC`(CX4K*!IB6ZeNBpL)HH<4oFLQ@r0tdKd9CNH#wXsv zM6Czq6Yp;{f7YiH=3>mdn!mfVZD)wL2ItQ~SfGp-=Ufrn|Eomp18P>G|JS%>et!pl zS#kWtK1r_inx9WQr#ALIqij<$#meChbwd{M`>?I@ueJJ0`5Fwjd~dvc-YLhY*IPdO z)iyU{I%xim;#8qGPCmaG<+p61`EO_Yg%ilv`(}T#IcJqmXR7|7`|;go?`ZqCK=Die z4fwTgWyY(0V{k=r{KjjCV7=x$x1B8)+b7;PdG0`o2CP?mB=#4GU!C$zzxcTEh6KM% z^SMRsHrY?$^L6;&Yy5Bf)y_V(gXz!b4e05oFy7#s}aCWirf!ftJ zL2k0u3t3;=25(wE7x#aJ{si`qmoXl>s%qfJ{_)oL=bzj^o__8hPk)1((9g$y^7M26 zc>1}2JpJ51o__8hPk+T0_I;Tif%Ukc4Op)j9q*L$(-~LX$3Bkx1IQbV|2RHiB%T?$ zr3p##BmQ*%_-JCx{;5XikN=MRJ#>zDnQZGRdv$UB6I|t5Zyoc6zJCLKj+AY3-EBT> z7g>KLJ>q=U&yF49eCp+$I?tN>*6Nd^H(>k9{L7SItM@g7tv;odzp}CBUuW|vSN<(6 zEkApmoo8~DPp4LYi!zoQwAAb)(keLr%s&BxT(auTN&2|J%Ef{yzhOg zH^GkGkpZvZqQ$F`4rT$T$CMw>6elNQkKiSW#Q2y)v zv>tSw#{}imVZM$sR`31Y)`DNvHZ~p4$7(PN5q4;0p1;oW9ltn!&tGRVB7Or~Y5Xqn z_wVZ}2jwG9eRTiC-0b-K_c_JeofFr;cQqeh`!B?A^$dGo@;P3!=P1kP`-ysa^sR9| z?Wd)P)%-`Myz z6i0D2wBdio{2lgW=pL?+4q{>FODDW`-tDa%f4^7 zgDpO^!=j$OdXVLFA8yve*5l)R){%7&K@j?51IX&!qi1@9XYUTFTZj}1>^fbkzZ~S>Y>;lWDK4lb$#}a(v zoqNK*ulT%Y37onm3HSZO4!pJ4_s8R}*@*t(ZusN$%NyYPqfP$%L*{YZo3Ot62kf5$ z+Rz>E8;o~KpIxruJH`9=d&BQOSqLB1J|6yZySU$n{eX$=r`B)Jc=%56N5d<&wc(lX zu(yj4{1WbdkMl7=6cIRJU;dB-9$drzit!l zKqS9^%Qc_Iw6D`~bn4&npWFP|@62laOZ9u7<*8CWouvNNExTuJG5wqPPn#mx!9G7z z@I1GP?4R@&pmpxSdjFp7L*jb*%vSA;-Bagn>j~b32B?fQ0_?$M|n~pUP=7+y|a2@ zG5nS)e%T%C$IoAh{TEJ!JRss%bv)z;5x?Lh;0q#t=0ubQB>1_zVn3lX+J_|gnR{XH zayj;A011BXSnPYc2>F2oKOg?wIminn_(}SzDujasKS>|pw~*i$3<7>LQ5F#KtCg~W zh@X2h?gt`%S(HtP_yw0CKM=1ZehSUrywI7UcyAeg?+Y{zL6U3iq#ozh)Kk2Zj2F^!{}S z2MKy+#keis{Wwc_TDzJ{qbwm{@_R0ubT@B_M^^S#or&aA6>8cR_pv} z_VD=oqeksYbINBuUO@Zci8g*p^ZD^y+rB#=+WrXNH_J3W?QVfyc>PWJ?ikzesZf3m z?N*haQNFMC2ibjW{0Yim&HOL4{g|xs=@2)%AMc%t?H{sS$6soGM*X!c$&X)UbzG>K zW<*!U{~DVH`SUxb>iE6;v%Np>TczVNpO|$E12Xrp8Sdux|=4XGQ#oqs?+ziUXe_p7-M@j+*xzv%x5+DpV2x(XF4 z`I&3FhBRLC+X?o#S0QiEc6z_yEW`&bo22(gSPiJ_CwPD3epSo5hD7^7??mix2%>#p z3gr>H1M-OdW_Cf>kUrhf`prDtU#OmoxYB+4sP+qr;X8AK&A@4E+s|vS6MPH%+g*zK zWZNU|#7pA-(~&+JzT3X|9zuD^n~wNV|B3JipB2K}qgokP+`X5_<5xc%4_}zL{}sSH zI-b8I5k4N}k$kI{V}DT4!N_0y{!NY%x)$<@J!9rhC#2I`qWtjJJ?Ml~54@&S1BmaZ z6rSSQA(eaIHD7zt|eefaCpFi7_2OhvwLenb5R(4Xv9(2sjM+x^To!2TKj%<9hO z$ycquR&Nb`UeVe74ddmazbU7yaqjGFYWi7!4g6X7A4d5lJlzfYeOG65JKpc&uYupa zwX-SltUuiyx^qWovl8-$;qX`8*4dl`e8TiS@I2kpIdAV;1HXBov+3Ltc%yvjp77t> zId3Nyz@NLXvw0EWPC4Y+2L8J{n>QO($reNP+dWV%q?k89E6LJ41P2fj;+t5G%+!pbry`z6=c|4r<QXzkWE{}kd1u|MXIfqgKD{jnPS7Gi&_ z7IB57Kj!S+$mH-_i2boT#1&%ytLjDc*FfgkhJF73ZToBOi`CZoKVz+|NjA1s&gXUD z0b|Nwd_8UEl%y4Mk+g9OR%`y?)a7u{~Ob3 zA6p-kzef4fvvK|k<#$iSzgzkH42{RXNBQFu{FTaoTI-wjw@Ud*$5jU1wNFlo+0U1$ z{oe0c$aFySy#1cPx;VbH-y^;+t9~Wzm**+p)&0Myzxnyt&Cy}P<%Iu@*`;4xe&W7n zQJjxqmGDa1`EdTRRKHtTALyorS6M<&(H#n}l1cIW@tGZ7*C_6^%i;5+!FgwcKF90T zUDoN{@~Hm0`h8aW5R!~5*k_nWisMWB4C1>?zn`*?49(wDehk#_FpfJ?e^1}t%Hw}& z_1)1_uc^Hmaq+dh`|N1rbH6<-vkR~DH6QY8lz$N8x3>AOQhr$%n@{#Q%g-tQ2#wG0 zzctElS!?5yA1Ggon|IdeFS@@^du?(Q-;CnB0p}NZPc$if|312D_$9Lp0 zmhbC0F!9|&%k4aB<5N%jCFF6n+y}4Ie6;>1D1Vs7=lFA#^1Xk=%d=4VXRDoy|EwEw zeC7M9A5N)#p4k2*?N3%|{G`M3I~tJdw9S`-@6?{Q91GOGa?IZm;m+oU=h^3S7GHF$ z!;7wbSIGJ3l;=Ox9-iga=;mf^XSx3)+lNC3+51vY9ZitmUrFnuoAP%&*Z;5!K)II)At1O!PNL z7Uu7q$BXMP=ik{r{JZAgOLZR@&bv`B{$2CO({!Gl`}a&$JTi&>cs%8AIbGWkop)WP z{G^k__o|&={i7JZGd{z3#U2HGy(j*6;p;85{VdKe6W{4-CqdkaZ`+o;g-`}s@Acv9 zDBz{)Hm?~Lo~`Y9m1pQZ0joG>ujCjiFLN{bKFhV*w$ekY;>J% zIul=KTaEmp>uhs-m*&SIWQ}clTy@<^HxXoccIR&nkO+HFrb9+829Po z`n%_C8b#lyT@+t`#;*Ca@`H9mPKkZaSU&$Aku7Pniz|4eawTR_jlOYt4Bw~f#Dm4=!j2|l|w!*rg3@rnDz z+sE^1q#1S4c>}h~tPg(IxoSU=()hXAHXq_exp&v{knd=G?yth~u>MX-eDAT}t;zDv zLjA@18g=2}Uq|(KmCo-RwIljxk@+3-Y;k-!zr*)ir_EI!8K8|^TTw6Gvi%;`b1(8= zvhm9`jl+4C=SPGb>K)hZW@lQx&T4$_dzDlEROOEwV#~wz2vy2IT=~Rzf%0F{{c(t| zr~EZq9`a9De$t^_bZ0NyV};-3 z8s^vUKJ=@~bBcX`@c!HDIuw?V{HeV(zV^eI?=Fe>)Vou@vhkTuXU)i28s~RW{-A|% zeplsB+QaglGW*_glrQ6c?0eN5mo^8(u++P?VCTOWABOa&<^aW48}^wptN)y-ld3BF z=NIHpo>Vn6Tp8bg*6b+*rpy`;G6u{a*nj%88L~LvsW--eqo>b0e)6oDb7#+*-hUK+ zkFhC+)}N&RRHsk9`SqVXeOl$rxfQc!9s5^V%&weXIcZMiQO=RGW=;RAh>)>z(hO%4 zM8z_HyVU=$vO9i?Gv|bvllzaKG;QvlvuDk#+IY#n){J$iA9VZnKBThP`0l}1*a*Fc@-c65Z_@NNHw-pHeYCuF zR}D5ohu{NC;-#(|Y=n5F^atHD=FOM=J3Dx~-q+Rl+le>gIoPvdVgFZcA2CcFUUa$t zYl*(Fu4)T8Fs}a()Q|IiqkRABMm$PEx{N{;@|W;uo5S6{k-4ok?gzg)+NQ5T z`T+i2PeJ@E8k;X5Zw~Hyq?dH(;C}80jm(dl-wM1KC7+j(UluOUTTG(?@jh;39+CX6 zY;4Ej@x3<(^&#avQgDMi{{ZV&bhGY(z)$l38TBsThvEFm){XN0wG83~-5Z%nF2Z}F z+5dz*s$((zJ>fK)AbXqDUraqge^dC=3+0mff${c2N{!->Jqw|+ChZ&u}lsU{2&+5I*UkXL%FKghB&R?c6XUXpf znZFc@&R@zLCfgsGzZ8njU&EGmtc2GN?GTdeb0W`S?N0E6 zuIT{(=CEJF?~r;T{R+(A4(Slm5`<^l!hUsdhmaN`+`Sw6Ro)?_pJl%H9E2l(p?yxb z_j3or4|+u=SpJFma+F7i<2?Sb4o2t`qz}*g8PvhlfUbl-Wf8w-SNK6`x~7fCUEeRH zN}Si|pdV9nXTOm4bupj12kZ&&?ibQF|3rCL;C!o_urCbgj?a-f(!00z3+eT}EdEvZ z_A^2ycUphy;eJM_(*dy89Rz!l9QK(3{c(;BcOO7{(2vI<{{u0a)*t=VLw`MEg85Vt@%r z@#Xu8?tx3Mn_jH{@W>VTemk*Yet)Ih8@3&GG#|RO?@n2DH`j|^0hg!$KzT9WULo}k zw=MeVvTxBM=xqS^idOIsZRnR}_&)tRe;@*M`#a*p_*4MM@U)bBm`?RBVFNXe0 zeAZ)?hF<}`ya%OU;T(YWNceDX0AE}=$63mmukUT)2R~omy$}!buzp#uq5UM{v7YYG z`w&;^?@v!Oc^UYYPSE}t?Q>qj`HbF%7Dn{89sZc^1IQCdJs*I5$97Dct7pNF`=`gk zZUBDjaSi-+1JkB*C&&doa>ilbGK3Q+AO0M|iAUDGQwX1iu4hnzb{2jYe(HU8Jj#Rk#HAMg4E*eeXD2{!5ubPk@H>O-bCAXTvj-!8 z_{R!v@CWeMB7R5F7YRQE^1)Ah1Ndu^e}fMi_L(tnZsmgh`^f)8|KGQr>>RhzXY7Cd z+wSlN0TCWEYx1#`NBuvDi;ooiHz3wFw3t(&OparHdHIpC^I_6EX2KX>KM!a7!ueF$ zcWwKOZclj8<^D0H+CC@eg*Cd_L)W!%+(x6DUq;(;81frxg8VsMI$qEAj{D|hmCttV z65V%z^XnWhdQ8Xra(X_PqxtjPFtdGp-gSxQlT|xnv})nCzm}i(C4TSexEcAb#;?!G zYQO|_XRVt5o{Yb((*1kN760mc5#f#|`Tb{t_cjgxsk;8&(emqU%ss;_&g2tM+Otqz z;yGF42bvG@q}_wB_-~;%cv$0ey|SbH2D(2B?^~w)k_4YPw^4r9al@FzdGiFHdiYfx_fN`xuxa@Jc!BD(&O5LG z%U_7|iN9YN=M(?R6XSg1KRdy9^ak5Ewd1sb-j_JL_r>!e&d+@x=M(4GcZ>5~&42i= zaX#^He?XjX(bGgloFx9^GHpIChJTY2uzy+v{~*{j{CCsx_zC^*>e_OWPyE*_{^S$? zy%YHm|9cdF#&=Z4T!K&h4@~4k{I^Mzhxq@Y_%nZ3^S}DSc=?IHr}fMD{|bM~P4~lf z9~a$UJa@Fcan>Ih{*Zl|+J#@3mfx=_`ce44&fU{Hr1>Y|d-?*{|F7&FQpHsWe-Pgv z_x27+`scxESl@Ra{2+HO;?KqR@%_C+l6^~^Ug(!$p_UN$D^5LtbLK$ZaK9|Vef${Xx>t4$wb0t9yrZBK%HE#D`z#ip|jvMEdl@y+bkK z#IJf3@{xd!Ev%S%V`FO2^!#CSrsHTumj_6|w#Hje|pmw+FL__{CSH^^Is@|K~zwJ0yB zTGAuF^GffKh_?^F(AO^HJrm;ruR?y%qy14|mo_v3ehcj>-&>1Ozkf%4f__GPXS*gQ zz;B^y+|NgP^E&DS(KhUjh5DtHU-$ER(%DjpEKyN~R8Sl>Fw@^m(2l3Nu zQ67-&H&3j^Jz#9y4>XEiQa))h)Jw4A&qnjfykGIckXAaf)e!?#7(jSI~Njd*lrWsu}Q|G@Y+4)7b zCx?~T_&k5IT;pGy;JeD-hUZ*0wfR*jf6Pm^oW4{jF19C&fUmzD#><}TYpSRBHJ8C3 z^oBk?kAb>l`mL<5rR#!8jXeWPe^>L{Q4MkUv!@qAQ&zSfV%uTi@{ z_nSD*tI1b;R_f!;v~Se)b5k`Ro)eg@wDrC~`E72t^3v|nQ+|C;wFVIX{!`!UAozPd zY(9aWZ!GK6vMXq1j^FEx<1g#hDILyei5;HNa;8MQ-nkJ>HdILg0+c7FO? zlqsL<`}Qu zFdgf4>mU9u`|oyU{I!aItJBf`O)A(&2A>zlU*;|3BL0>AZF!v95jH^okJkPh&rK>( z{@MFz{H}5QkD}d`;+EF<^KXggPy8EbeBRg9_?%}qzuWg1>m{pp)sEWzW;FgCT7GZW zVIhb0m3qY9H+PohXEpwLp5^@fM#!0g{>qK$UmBcm0=f#%kCfN>qgzNXEyec)%Im=| zltTVeA1V0_S}xDqQ1lmn?-tUY``P$ugbCfz3VzH_RwGH2$gfeOjvg8qMGI*Z!CCuU9*;td@uPO+Q%6ul)LZJ~pF; znjhVnFMU-S#owrXW-rXw`I8Fv#lg45@u&SZ@&9+(Z$G2^aOLi^?H}b`wRE>o0djv&z3r?X=0Cs{A!a4!6Jh+4xn;4@T?zO!*6x|KZDS_>1{? z%CFBUYiL90j(>3X*!bs!p_wqk;akK0&1q>aueJT*?03cSmvy;v5&wPEe%0UF>Qxws zf4eoedg86L`fO}6vs&$1sh^(m&rmyCZ@SIrbmc#x@%de_Q29M{+|qm0;#94CuBS3n z+^{G$${(xcWIk(^|80dWC*udo_tl=3{mc@@v9!0wryZ~IDRi{k0j?t^$AJd8j+kr!4R9SX zIUY2?b;RUOpaHHUrgk2J`Ah<;3FPxuFTZ;(3$`V|1@gYvKoWd1EkVf;T8?!ecG+Sv{BGq4LX z^7}!%d%+!$`8dY~y*Frp^E-Li1-y62-3RW#;o}iM^20vVu|lK&L;54gBdTw9?NY># z`8T?s3ja~}f4BTkbMDBMEB{k|w=wv`ZMz`nr*(;)pBCV}vh1!np9*047bfBCDuJvs zI_6uLa2YRl#WJyX=PZqJaE^=)dATvE1%Bp9+xkt1>t*ExQNe z2QVdS0?_%r;TMqet0cU;9pVS%{HicK>^b05l#jm|`j2Cf7XjK+DJySJqz~{stK?*y zmj>`WD<63k@I0$L@+zo20{M^hSJxtbiLY@Q&c6ZzjXyaZ@d4|5Snp|`wr9iV<;q|5 z7v#ymV%z;c(_;`_hGP4(2lN5}wm+iBAYl6=dJF=#KcdGVVEZF_3<9=4qQ@X$`y+Y` z0=7S*#~@()BYF%1wm+iBAYl6=dJF=#KcdGVVEZF_3<9=4qQ@X$`y+Y`0=7S*#}ESb zL)stFV-T$G$*RL_HRqi4tLTqZ(SLr6RA}85^P2g{{tEBs^50+Xui*Fha_(CgWc@SV z+x4DU{~g<}V-D~AY>Utf`3)MNnd z$HlRDGU`7S^S?;>w_aKr&l$>JVsu_t$0LiCZ$A3Z_65wFGwV>?*oW)*X3jtKfI8fc z_a_?t(+@uQ&$bZMr{<&&I(W{Z2aOFq{%KU<=74$5A=A_Q{7HhLuhjG-XHWZ=8x6)B zdSL2^!w#7<@1Q?R_lSe$%|H0aKlzQRBaisA@I~baoUVbA4$G{#CEyjNQSUDJ2ijwOHe4^h1HT8jQQYx+ zo!gK%;7t9!=3bPD;C7pT|HlOG988$G^DUiSnZQ~yOYM*Q4-WI6?O(3^Yi&2f@O&LP z$0u6;qdl8HDFb;LK+Ak^@JojfA0mGqey`W2V~8&_zt%U-=f>~zu>ZjA*fGRVq$h5< zP92S465F%=QF#@RzTg-5cNaC4zlZoG zeCpQ@A>#ERjo&NzPyf&%#0C>>`pKU<7{M0EZ+M?{E975*@{f+EW}^OxJR8635W?ug zdQbDRk@q}UuKeZxFL_+O_e=Cgx<>A|?v7!VhxOyaXLTWDpQW6qnEk0P#P?TQI@27w z3&8Wo!av4S{^xZesE?3-r_KoEzI1a6^g&<`f%K((8b7QH(HTpb?op_JIEYRl`XL_t zHzGd44AC!m4DO%89hkNU{67x)Ab*188(})(;^FZn5aiShuPSvI`HAx~JC_{VamdEu{TykD;T zAHB$i$V}Gx8|1I6X;o~0xPIcCILHVJ@Gte#9X2=w*Eb5s!VTd4^3DnH4{VS3Q|^;D z#}5ke%!l}Wk@5PQLSuaHZZ;^ySD+<+`)k&%a}O>3{a>7Hf3LGM;s=_2WNB~TK}Jx$ z!qS;pgN)$)Yb~8SWRMXghT8Km&Hl&_@D_fLq?OugkP&>aS7~}L?r7cp0|ptv&q!bD zyOTlszy#z^!Y92!M!<*VPIE-xY-PFfzYp{G^0@oCp(*G8cf}uDGM@i0X~?q~9ArO| zrPi&J`GM$t0ADXec}B+H|LOezdu)#Sh4~-G|KD|A0OR9*0i>sGfc&DpC;i`be*nv! z=3|2|T~V(5|GvG>A55_IU&g6jF@wYMe+!lX{m0A+BST1kQub*iPsDu;zv1#-WfgY?p;jtn8~U--QR9RM;vWH!bAsM8Q0 zC?NkH{3lOGdVusVeb71B1#gHu{3jQ|9r);Uq>uMGfL+k+d-g;BA876WPe=5>ts}Z9 zY)zW((YQG`sX97;6AVp-K2C$je0Qk?_Hj0_?*U%R@S)ij^+2Aaj=$pPpjcO9w$b%Z zu6yu3+DrB8$#*osS6k?O$y(bHODcclYo&a=7U8o@*Q2-&<|_Yijh}jL4az?z#<#!P znCUV8RP{e6#`lze68SnmG+X)CYJSXlHvehmFIRJkn!DHU-#1$RDP2GBxETHWrLEK# z-ZfFE!ozZ>$@35YRrx=~bw$l5%RjCU<%Fpi(@YwY(S>^jN^YiX|z7j3} z%8{wi$4%(J`5#uOZ?u)2%9p?BZ_2~+zjv9g_s$$s3@euNjc?DZo|4Wolcbc>JdTG;W`LEIW-;Xyk)>o`;yFBeu1=)}_>t!mJv%n!ILeRD6H_iP&e!)s z{ZlUSd1A^X9m@~p;_3XcADg#+Q0I@0XXmq7{?GNbAGM`AtQu5WZVBZt z)b`4I%ckon{|x14=D8t<<$OqmjmMm6>nY25jh0*fRQuksoKMv9r+%)d@vmSzsq5z~ z&-izZ<+;D=HSm30r19(#)AwAW{5mx&)ckH*_miaj*XVbI`){xM9Bodwc1`zRceUlf zF9J>Z{#MrLl)EL`oBNV)^mPWX78tRiS6Gi>PM{8_COP8 zxXg}s*k9-V0n+Txl71c0%X+fV*^!%@;X60ciBX#u_hZmM_hXQri2WZPI?+NGHrw3% z1qDFb+hX(LdNKW9+yVVj^)Eb@OWdTqX&!y_HzoHQFVXMAyk`+|ulDAyk@o#?yW8@{ zG)(x=ylZ4LjJYd{_nWj=JsZELt&i&6P(M3Rz5j$dJiV!vL*@bd-d8IBLw#SVPgAG- zobGeWTx{jzDxdl*{0>Z2e*9ckPx)2bmCEypcBOP1j2~FGVy@Y^i7w!j>LpGSE$-W* zo{Gt5@=>KU=L_hr+4zIOdQ0=es*f_|#&26+Z-e?<+j>16&u)(ulMLKItW(0nRVE*WmX6ufJ={+ga8L=Rb8aSGKj|z4TegFYr&Oc|_-5 zIv{<@Ld@RyJu+XFx(@jT*7r~@G#`C*{O{%DbMgr`hWv)Me<1rXx|yc=rB^0k{Ri{I z!EwDqB#@*t67t%ocZlmDUuOjN3r%-I+zmPd+U)0qn7IkgXMlfmXzvg^o>%&NUU{#S zejMTDcjjg|M)0E2KH?K3pE>=Ian6O7_NwuFfDssKhVLW7y1-M?KsAHDe4pMSQj#Cgxs7{=*cY@j8tHF@ z^no7`ztB!^$OCv4)yjkapc3T^TrTM$KVDy?2fTpz#Xa2<;eoerwC>4%y>W+2Z}V|K zydQASA}<27VX6FDoNV(?dC7SvQeR2;k`F1ulTKFK{E-d|wah=th34kN(m$1xPv&78 z*dK<58jt8vcf%jQ8$2g1-PahwTDr?cjUJQ z?G5Y#GZQr0pHvO@bOB>s#0Pf|cEPQP&p842Il?YjI2e3<-(8fWU;~5~It9C+=4zxX z;b9kS3R>JVunTT)Zu66cT_ES91n{4SU2w0Y*AMxHUBL6z8etbql>8w+uNEc{wx1iL zzQ8W1l>8uk26n*|_?L6meAor+dngy0n+9**QcgYxxy`~T{PVUC&Es!r2I(~I|KPch z&JXptXRF?vzXHDt=P4Cn7yN+vx&Y@b`9Ic&kov5&@ z28Wf>LB5oJ_UBT1<5X#T_C<$M`rMaF{kK5=qWSUnE~Tr+mD1?qmH2Or<;Rc3-zE0@ z!a^!Cl9{C;jWX0k$8-^Pz9r7vr0L=+yAp*vhGu(lkYG?G47E%3t64q-(iP zK<8H&ZTwtOTPyIsJ!132{uruz_;9~d=2Y9?V?BFE){|DWH0HZ{6U?kP<`L9W(2YXR zst?ykS^mN7dh;;q6KU_PdQ%@MKWBcuIq{<=ck%aEtT)iVn3@wIp?QCsJLi;pe?)&ie7+tV8lorP4K}uKb(k3PyQ6*Cy-$b{ zpe1k49(^#+-pAZH3ht*itsfk4i*-*_By>+pAfgbY~xGq*~bX(KzM0C z8h7jyViWW~c)y>dbLhUM<+nOk|6R?G_?71v_>X=c_V~nxZ=8Vm0p~QOaX@4vaD?X9 zhQ>P7Qgz=I?!bOZo1Ob$pG_Yl{k3TPiGkL?nFM!WLM%VFaR2z9a-sQlFMn1!`K;58 z1Hln)7>Ufobc^io=lV$S>j)#@`bhf65k~NywC`9S$^SMYL~(tj1=bq?u8%bSGQtSB zKGKM=0*)uMEk_yw*GGbNNDr9M0^bK0>l5%R;QC0xU@ZfaA$gl%z971uySs$P`%L%k=a4@*oAI?{M|GZ|#+pc|hzFK$Fz1Xhtd^L9#^n$lc znqk{v-{2s;C!iCc`@e#Ey$kB?Z0HGZ(WJMW2c3ock}Bj|+~?rDGq_WaIS<-^rrxsq z7o3+i3cAZ^7o?rb2@QAZF*|dS9%$+@XF;bBKlPXs2Pch(@C(q+1V}FcorZtvHG797 zao!@(AMuZeJLn|*Q;#`yDBM6(Z#fS-kN8Dz*}*!hdl>YtL2r)V>w(ULE+9UhOX#NY zdqJljx8;!sodeB%6fVx*P9gsIQ>P5d=YWTnWuTCRWa@K-iO{#z>-kwbSQcVya!^J%$PYmLCEw)G}? zdoAvBtu>!sf%h8iZxGamI2+lK`6=fO_yuaw9}{=e4Gj#ipue4u@!zdAg4d6>?wRiW zjbN)cZT$XQa0f0yeA4bEUat*tmD2v3a0gyM{-yq=-mk@e`dU+%VZV2#E7AknjY9qF z-1I#<2;rsw<$hEf;>U3qUm$vK#Q7h-|)2~=VSTzSb6h&vaP;GovUv;pU-Sk z{{7+ke0+aeYd!X3>pKtV`U}_JTWb1`#P}RnZJ*Hkr}3~oZ8Nl#k7=av*;)N_y^{4W z-^==Ube)3rt6$3UJ&nIo<0;%#%6F81o}O<(|8>gm;g|YPD*y8ZrF_2UryNkqchx_Q zscND5+&$G_N&A-XZNs@EQlSt3ZWtq*=edE(ch&y~F@A&cH`Venx7haFmbN;=Si*{{tQkjL6TUy`*!h0X?uqv1 z<)-uf+@b%Te16-lwEURzwp*6+DW5>gkL6AIJQF)-uTmpA?x50m9OciCogYZ~B-k#f zoGG708V}EfV|kyZ@sRIo{Ff*{pF=~4hvi)nJHKwWPINf2bVc7-o}}^_&jRIJ*FWm7 zZr1193bb!q)Wyy(UUfCvxA&UXPlKeDk9GqLO?xg~qIj?Bl8tQnWwy2N3Df0yY5r2^ z>jKh`lJnAXSJ#E$dT077==TCVFRgGY^l<^6msYq0G{Es|8g{`KF6MU@V!rx9=nDhX zPcB>m{{YWROD}&v(rPh4N7LHTig z8MY^JeVM81|J;S>2;uQXfOyj3e`Df$G>eoU*Q4>3|I53!e!3Iv?_I9^@m(!Hzmw(X zl}}@pT4;7~+HM~0UsFE&U4#C`GEBPbyYr~gy(@jkZYL_Idj zCoTud=kUD!UHOT&T>p2;hw0H=apFx^M&&bA-=CHL!29!Yg?Z&t&DY5C@_FS_+tTs< z`Ed{ZKE&p!j~P?SXZ!fUHKlwu$E#*(JGt7n_Z3=CnoqKP_i4*dC_i(b<(p?Lzf$>e z{R2n&JSUj_fTZ#}s6IsSv3>vf-p0?3cGdrGU2MASH#R7r{UKl5iA(W7rDnMI?u!PIrsW@xVNcYaeXcYyMXI+Y1jo^pYvfCaD6TZ zyMXI+&JFF1V62Px7wct7*acjlOTjMS`dnf;;s&@r=fE!D`dkur0oUhJunV|8=fN)E z`dk)v0p|^T*agM)I@k+9alH=qMqr}k7vmZ4M#Kjc*Xv+U1I6_^*fYQs_z&0XVD|w& zlnc${p15?&a`L&Vt*zniv_F@R9eu4^$MsXvln-gjhcx9wn(`q{`H-f3NK-zfDIe05 z4{6GWH048@@*z$6kfwY{Q$D0AAJUW$Y08H*+d2ztNh!juei{Tf0iqs27eFDG2LG*yFR!3MK**dg2p5I2j$wg6qW)7 ze1AOH1$=+x{7(VjpB(H0zCU@`1$=*!Lor4G`2M)C3;6zcunYM9I4D>F-=8Gx0=_>k z>;k?&9_#|XKbTfF0=_>!>;k?&IoJh!e*)MAe1FU^ zQ=8%aY0}@xVM@=(_pq_!pb%YfKC-h3^jA6!3Q-AnIWO5H28ED&rM))Lj{+??5b=w? zPNwsq5OV)qPyu}b(1Mi+pNIcK7wpdj+AKtRSai*H9Teg?gctvbZrB?M+`gHuzm2d9 z7FAi=?~eVCz_D1kko`U+IbXfezi(e?86r#>%O^hNJl#^eE*1TAp9?v!1`k`Fy6^TJ_M$=l9{K zgl*T!N7E5Ly>$Np`3=gijqyF@AAYO-4$wcp0}GBQ!3HbyQhPjjp50T56}(sILuJf55t_Ai9n!YpKz7)Nm~o^`Gmg zvX&ZMN0qhIl66$j(REZ=OO39h%35l49aYv+qwA=$mKt40m96cmO>)as<-R`! ztjEaXUSZ`U>pwkAlm6Sax?U5UTVn(xuY~8nLVs{+O^C;L!FO>1+NaZNLgcaD5#YS! z+~S%LUt<`_`XgrHPD?jlP-6sR>f!&_R>r%eCPcHAc6}wgUyTuT2QA|b#&O)2DBlSE zDAsqP=M(?^Y1Fld{=&1!Pqck`Mw*KA(O%8!7vi+J&BFT|8UIx=`Ub2=$^DLW@Ag$G zy*;m@x3a|_0 zUS-i24qz9sKbY!;{yxC|U>FOAM6F7C_mU6fuj6|AZ?&1 zKiHE%QGT%FIM^on!JY;vzdY)tfbz@2O+fkiunQ=^9P9$huK>GXeGlbEbM){7H!UZ> zozZ^{pD)LT#`@mBBq9<}etCRH1bqKI^b-V>UkY{s<(Gh8K~a8)D1}_qP_r>p9{Ny^7CL9P<}?@1}Hyl2{Ho8&uNErfuj821~t#3{2b(6!1vEb zUIdh%2iXZIzZAj>C_e{j3n;$~!U`zAEaDcd?_s~2?cg@Y{^MTJk8pFgpPU+&3WLh| zANdOGtK|OIiQ9*K`&MFWhbF*tLF$z5sq4$maXTo-8q|lC^Fq9hn)G4itdLZ%CVf~r zE5vL(qU5}gOy3bjeONgwB!%$Qhn2HJ3cZ{3Vdbn4AO5KiD`$oHPLn>YoE4JZut^_Q z&I)n+KnHiJDV`V7i15+#LW1fheONg!#6|j|A1eJHIV;4kYSNFDvqBn?f9l7|Ss_`( z_YP#j^Fj*vz0{ADvqA!-|Iz74ANoCVR*0#gHb6-ch6F@kV&2p3g0k~d-KqcDuwCe1 zi>L-7RvH_@{T=*V-0SuMH^IKA%Lq8axJklRQ zzZu~At_NLU!LzR-J`dy7wm829D9Y#S5hepnKzyRFoIqFs^_BDAAbsGv&un<}3Caeb zyiy+{eSrGP9^3@fSH>l^XkeSeo_;N>+6kDKJ^NnPTPhYSNU;0!Ibjj`hZiFAJ-pD zE8i@(`44m)f4K7FdV&j-KSlKiGgsU4%qag0-5>6JzIDi1r2M$yD}&~;XAWqL$}c@a zf5{Z+Ex%jQbpPe<<>g2HGRiN0A1Bj~>ou`vRI1(-e}6&)RI9#NX0xqA4&|DhY0Jyq z%MJNX_}`e(-)cF=&fyrX{&{{(QvE-z`YxFx?Dw7WJ|uRJCgn70;Fe)X=9BU`X?M-f z9iv0{jK+VZ>Mv#Vei6!}LG$Cav;MQ{f0FW@9+tmE`EjG5j8$qLJE7sXsQgm8zP$P$ zXm29x%g&_o^5gn4<@brMzc{)+%k&rMdzl$yPzMH3)h=8pNrIg z%T2XDDW7s0+l72L^hr78G(T*gR%twO!&e5)86DnuEh;~MbNfpky$IjuRyd#F98FKp z@ngG5^R9Djgp?o8CvbY%dacQsOE+b4H}%JTpuq8%KNmM=Biy$M z1jrBlirE+8#dK>CKd{NHQuk3YdixkX8~Yh0JfFr`{5yVU-Gfb#Hc*8E6Tcb!7Qv03 zOT!P;-V_(S9**F@Ha*lx`3G@TmJ|`uoXC#qz7g9u9xRPWg@eV23@*$uB$~ zL}b# zElb^_=XXdMbd06f9W={EOy9we!+a-sOF+{u=7zEGPhMN<{tU*5(fs|Z^FfZCR2n{h zUPukP`I2s-1@s?*<;ai!Mhj!w)L9_A@=$Ix@7thx+4EksM`;?#+7uSEYFv_S6rbFXh< zk~ibNK7jip@^BMOL-)Z=LuglHvJKdGx()9R?qviGsNdeXSRaI4FhTPF6~ zsui^oD|C-KEow$>&FR9OTPX2)VN`N(3&yx8T@gq-#x@XpPHG(#po^?e%9caUo{vGR?w2L`K{X3|0 z0=d5>>K@-`CHJ?8d-~_DMlf)ir3)>(;eEimvgU_zbk_L2@4Fho?PxE=E%O`l3v_K? znjY_a@qU4Sz(@HqE-`l)Yu8sY8+0=QEa8Uk!MC{E1>jSs7Rs;F-otm2{>vKGyXN^< zbI=|;)0*_R{rzk^ifo1tP5CZ%F#q4(f7fsseh2O`&%T3u!C$E>-m7ii!#&h5H|c>V z-^X|MA9clfvOMTSp|1EH_ddXP@bC8AX-6g>KE!tp^sBq$J;A-`S>cV@F@izt>YfnuYM`rsEv{80#OeU-)#?_yf%krt1In z@zm8-^T8WF=qm4Lx{pn_GMNfpa`&VCez&DLP{#%CjPmjoy;pfC-&1s+f%}TAE9Qgw zyHszF@~YO~MgNpb^BAAy|BRlCM*of`@K7v%mgh*_cavGMRp>LR{y#a={$BD&DgOuM zds?2Z@^d<_VL3M_e|Ie(j$g(rzm?{n{?QzVPwyCiXXUTa_?fPi0(4LG+)47Ms{i#F zELWP__1<*ba^KJ6Gd70&oTmOyW#s&8_J8vDHVfFlNe=A={XE;m>zyiL)c0gC;V0`5Zw*k1|YCNKvhe=S>^##gIC$oXoSEnA^YuL|*l z^#8WTd1U{r3Q?|qBfrmit13h-rn%+!xj|J3_IJGD$PaKYUMEQ}@m^I37wJj-=3SJJ zU_H+ggF(22I z^7%fV)XnlaKfw2EUu`FO-#6c{PxRbO`gb*fMvb5E5#O)m!8U&K`F@SkaXRzK_a#0~ zpRMsPVm=4#5ONkMf4u%)U*C6@>y|f`rhB3KUmMFOreVToca4Ynyjc0)>hC5066LSY zV7VsL!*)MB*h}P_9cc68YWp1CzxiFeVl=@lD_<3-p~-tWr>al)ag|rcvBj`rnKr)k z_cHyorpx?eSROvqzmmH>{jE#y_w>P|K9$_<>Epgn-mfQjd-~u}ze?`*^ueP(mE7&= zgGYTTx!cnRkNQ+{x2F#t^{3=+PaizquP1kV`rtYJ@%_TN6mqwx4<7ZY&e}oPV9b9Cw9N5v+EEor9T>ZWzlSltJeIA7^s(Q8 z4K%=WZI$2G@;fO1SJemf$J%ih>&d)NOY4uT{^Rv$jPgfJEA?+CpqP>&tSRI z{ek7P)?)p+Irp!!|NMCx@;yrKk8WnFBJ)P6jZ6FE?zyO^r!~#v$+(s0RAmc7Qys1Ii~jZ>e(5BI-pqW#AZ{tQd!&go`R(ej@TI8GK2Qi0!Qc6$6 zdlPlP;$@UXBtHfn+Ne9%naSG<6^(W=TVla0N{ikp?;-Nf>SN~ZQR<#^O6lkE`=jA^ zh?UoNvEMs779ZtElix9#_YbdnP~P9ET2EGK`z+@(Wop`+X6w)q!jsW+nwo1wGeihG|_Vo{nL^rdd^|` zjHC(jP5v9_Cr$L6r^1Oz6FuiCd4AITt8<=Mt~3jSwtBSO{&Ufv%{zRcNUbDo5&9Iybd50_t}qVHZ%pD+RlN z`dt~=1=R1#!Y-hGmsx>69k2rJVCF68Q?9EBL4B_@+yps%7Xr9@&Dw_8`grtLmg2nI zc5Op^`YhUc&~A&iA;!9Jhkj-TVFlC=^I;cIKg_$ioe@w!EDgJW`e7N^1=J7oVHZ$8 zEC;)Q`e6a=0_yiQ!Y-hGm~#ut51@XS3%g*V@l!7xOGN9Hlg$(QdBumfu{LTm{KNoHS$}b1IpkDLWh`b0W&pg}&l`|2aysxkeDBnid z1$-!1nu%fCjxHx(=|9Wk&b8ms|4Khv>ezp!A1&ekZ|+A+{{P?VM`uE*HT`S9WdEx~ zKiAJ`zjv2Z=y=_8Fm`1x8FgrZm2*Oa(mVw1wmjKp);yW}pJV&O=fwD|Ux(?uG3S36 z?^RtjKRPeOb%B{WzfJ#~ubCU;Z)me)HgB)-Xnq{!uh4P2ulzdY&(Zt@x~`E_{$2F1 ze2zaJit#z>U(?5?%XnOkXKT$*ew5`jD1U5>KUMkTWBl35@2LCjy$@}Q3zQ$Ixk}B( z+s_*wo!^+P`~R>10qsz9|Nmdh%a{BADc=bl?eF3~c}>PlitY2KTsGwVyq0sd2DoUf zmXGRHQeN|8=K`SV51;)te#*;H{_?(>pBwD@0_ErxO5=Cc|MBlxKF&yEgVsOK7g(F;7aU!(j?%v}83 z@vEZp&8mKA&s{P8eG&87^nYA=`Cf&6dh$@dPjMc#J8QCyprCwbOUtjYY%}X5`+FVr z&-Zsky&ZQ`Uy${o@Hgw9d`II6lpieEI^INB7D5$WL^id=B)1EzxJueewa~i|&)p z!acfA-XOkT_L6c&ey~Trfc)e!ug!h({twX0!z<>aA1Hg|(+D5kC-1Et_=kP+KINt6 z?jP>?K=l31>H1MBk93_4%xu+Lbu;DP-`VKP%ER~fbe%_KJ7ZmO)t~a6-?t4}l<#Ak z+4^8++V+$0Y3IGQp6K_^(TKc!DWC6Y{X)y(MLO$@-yl`t@-DA$|B{z(#HA^+SvE{4COayKDl0&RsP?# z+!)Ui<+J=)zPB`ct>pYS?tg0Kpnd+nowo90l=+o=uVq}5-(v{&Jq{`AX=Nr2F_|Gl%u*~+INP-_ zX7Uh|tRG@_gzAyh{~Ty{6RZ;<<8p3qX3rtU3?5?l?O@-LI|c6jh7|Yh2`?N+s zBn9`Wh=1r1^EIM&c0>B0-6VJe5g%xGFT^)&NT{zP`*_^FhhSYC>r|MJmi8hKIzak| zBmaQ|_kFOAg!B&|4;uNAKDIo^3XS|&YTdez_O;5;a4E%_sjk6es#wXLl9o_=p5S72u4YKI3L-?1hwEhN26qW>79wF_I+}x!o(CEjtMM03>4^KSJDS!*tb1lqN9>2~XgVpKfL$!prY<4u^kWzNQz9gMVPT#-HD%qY=D_^7L{4LADRV1M7SE{?I&N*>5Au z$v;?bV{m8L_Ey&UtIO!O2BV<=08qa*^HH@CP`@?!82Y6E^*>TuKtBUG73CrNt$El5 z)cd%z=w3!Z{f`Xn0_uMhKCCtZ)@QSIFC&u(&&fV6B+ir)@B6@iErN#N{TfVmMkGx~@9EAGpY^?3hnaxeN z13^dhU8nn?uWM?zhQE#cu4tmi9b?}Ii7PV;_wK;I*!k3Lh;|h1H0cxJ?rVC&Ta9*@ zZr;t!jei#6*Yv%q%}f&Eqv@YJzVv*LQE+E?F+2Uel->dPjk=!`%il8jeVU)d&dtox z_`F4b-^~$siDwGpWBwa=!dei@ljnuxPiSEtfPc~n9CG}0B)t^eznqRR*k3Dg<@dvU z8-9=2`MemDkI`qXngumK=)QT${noQ}{MF-I{GOn7Q9sixMuo%ES;Nt!e&%wWKcSvs zTt9PXotJO|+n#7N=3>=n^L|DHhllm^!p_>C*~<<2{Jwv8kmYlK9{VeQX;$jr2}2pf z{at~^Q>Xm+KEkB(N9+Da_KQX-|8~`9VE=8j^6ynX^Y1GEP|c5bxcz=MDE|oNQ~QTm z|fAwG{D>eSK^4TA!-1?ns{gdyi z|K-<~@~0|)w`Vo}x9qq%t^9Y7E9Eax{-zqgul_U2zgOcYf06QM#Nwws=e<)J{}T28 zYK5IwU_8F^H>)b;FIWConooC$&3{h${Qh|D>adPH!2I*LruNC(d4NezYNFFq256|J z_p3rVqrZCp`EuX?C$6&|yq04^i|F_F!VU?_ZL0}rAJD#0(Z{>XsSa0jc^nEgsK&Ega2*dAK-dI05`z}aF_ep+y>lF3$TA+#vwi65h*U* zuWQDG26(@2Zs$%$!2K|Jx04a@e%&1W3V5GxZX#%adL()L76JR^F7hJyC(;Y^3%3No z`*l5}CE)$K`JE6RpnQD%Hot z+g|UQY6kglP?W#yhsoc8^t$y3A@cUN!uf)~cMl=+BnEW0WseXpx+Z3~R#+?Q5#n1J zm!zQII~d`CZ}tVfWlJdjV?YEfssg-*?c7W;oLb_ytu(!n<7%AAqiP$@kJ9SV}JkE&1p7w6TAwduCJny>%0a57^^Y z>z?n?!w8ljefd3Zcen#D&$cwiX`# zjNfy~h_6~<)8+S8yAdWAuQ|m z!s`$|TvsFCRsPqi565)zI)%^L7=Nnr+vz$G?*o{v{LlBZ^_TpN^4G@rOO$^q%SZcv zIpv=lnm4bC&mVmBG4gYFJLo4%M-6E& zvF%424MMr2k$#1pWlg3bsm^}ekaE1drUsRdpGEOJNc+K;d3tE zd!0XL%Xhr47YD!C@hO_?@Tt57@`=nxWFIS~KR^UtWd36S{72_;FBw(p9_qV9{6ExC z>i!eTGn(GnFPGBoKPjbsi9eFRkLyb5#u(jhX{md|#^^sq^0V2nQd*1Q4@QM@Xuk5^ zV`cAGXpR1mJeiMdJ0QY~GZH9#C_l%{p zorf5~@W(Bk?}mAHVA4EGr`uv(0GRZHy7z=TaQ1Ju{^xoiKHwGKy8GQR4-eeCo29c3 z;s;JU-O`DThZw;@$7_7;5g)Ku!qP!C=JkQYH2p%WAx7Y9dg%_x4{$%qSAI{XE7lBv zYczhp5`KZ(ArEm+_kn-lBu&rn0(amLO|O8w2%dz0Y3H4mC_kXfc{Y5m9ey8B(DV%Q zCU{-_C!1qk1o%+h3kmoIYL@En>5bn5EY{!SZ8!vHP!2ImuzxvyJ2Mf$^Aox_Ct+;=Qk%{7xaGx?c*+J|Jn@=!SkCF8^9e{ zQib{s_jJY35X*a_E@Iupg_~eo^v467zwGXadv5{mH%h@S;Cap-!V7kS-nqPQd4v^k zKavN(0`5m@MBW6Q(2&Ua&S|&_isw5ct^nZvq(U3~KEUaZ_+>xRINXy9a6gg@yMX(T zGO!DHzH<_J5%7HHB+5hZy0k|V@I7oh)Z%elUU|?0591AQC#)wRF2Q>{l=dHP(eaL1 zJk$tI{S^K&s46T&et`$oJu_}7@-x)zI2`Q<+|#?Gyn*#Si`0SUJ)?ihMEj?e!&9M; z@ciiQoAl>>Jx3bNM)=U=`EPmsV0d@hepB$PZFkhW8Cq}o<|NB!{ha)?Rbyy8oT~E*q48(VO~v)xVVj!p*S!K=o&) zs{h}0Ue~|YmYb*iJI=KJ-L2e^Gh6xnuhjUpJQpZGZm>Kn)Zxmls}7U;v1%h5p65g4 zXTrm~P3J@8eVY${ziHb0{W=?8XI8Qd>^ z|3LHyn$C?f58}KD_~-r3KIk0irLvxl@X3d8o1lFT~-^p?`4_=tn^V^YD8ly!%wY5F0K=dRSjiU(_$eDM(++ zJ+~D8fkAM07eNm9!5w%C@rUm4p93ZzWq)55zen(12jur$j1QLKw*%Ksw{#kIL00o; zC9`f_7vx{Qhw0UD2WB9?Xn%B+OJE-8NW~b=blFX1V~?K z1G}KU7T=dhd@`Pqum$8zz=!pMX5*!9mvZ%E`=@P)U?e_#cx?S>(toJ966yDX{O8w( zkow^FM*eog{mcOOV`b;n8o}Xj;yt_!}zs_<`b{yAb&S-bBMM?uC1BzckPZ z4Qxo?1R5Asuk_`hfhSs&mglS8F@D{kNglI3^!G5SU)U4=fmh*Q;tT#-YXsMxY2$0W zuGR<^;`fK{r@$S^+===ci7yK}%+HyK54a!qKM3ufjr+2JFEoDd{8}TB`?2Nz2X{ZX z1HOh&X7T%hoYL;Ga0gas`AVGf+=TLqrf;SF$M)mhh8CBUs~?A=U6UvC+4i9+-;v&? zX@A$dsG8Gc?o|7!UH@n zIB_M~Re4jTab4^<~Pj4@P8}pfXr`r zp#9t6ACUQtJZN(}z6pTLZ#a3B-yJAFpg+FK(C5g*E~u39ko`xn3#P!o>>q4|UBIV8Ei{`22UV4;A4|Tl9)o|nq2+e82OCAk zw_IP%yxiFc&U^~_#rH4^yFlhs!+s&`d4TJy-YcCkZ`irGzM6(z!18p~bjG|P{I^E_ zvA&vwU2rz&;4s|3^J?c1TwhJUhVTH_SKZe;8v)l>-8Vo3Twl$@F5voV@=e4K6xUZ_ z&jXy#DZnn^`fBqOL=71_6-rgf9VnQzoY$!wpXaU$0Pqh-Hg)sSD}9@ z9?rY=W{(?s3U#h>?e7tp-p9+=p6)`@VobVZ_<@O#<3;)@3Ol8?uqJVCi6tA;{ChM9p&rK=mTs-tUrgfDE(cUjJaFOpX)5FCu?Hm z;Ant*bw3@;XO!~e`|nc9-{wb~F4ui9?GQd^>Ap6~MY@Udyb{}gH_5V1zjI3G)1Y<} zK8IIZKJznG`R8v^>OZag_hER#3(5~-<^Qqr=iH#_&b0N=XuYXWcbXq%{xPlG_v`I(HbDOOrtgl zV|ZU7!*hOy^!ny!=q|^MO8+Uh560qx>G-}y=4aM7U&HWPjs9S;UeSE%xB0u4t6!&F zV1v4m^C_Lk{dPQ`vVgM`1>B$Dj({#b!2Jn1*ahq#2e1pcKcVsAY9rV)gZ|J~tuW@r zeh1*>UeNafoy$Z2A9!Of%D=X?Nj-}5u7M64p#RVx`u&eVzaQ8a^G{}4z z=G(HM1>C=o*bF)r0QWCsU>9)zg4w(m^Z}aoFQj1?aQ{LQc@wbz+z2CCIjPx706 zr#gh}hmicaufrXvdJ*LbI`<~z0j%#~y`nkgj_UE{>eurpX#4fLZNKFHClWW+ycA9let@Go>Cjq(Fz{!7X;1G|9hsg1A;5;Cs=|M|m_KEU! z=lLFv@&eX^&VzR5RfVXBfBqIR=ewv6&!YaI>;-JEOl!2qK-gY2L-+-72ku1rZgYGO zjzM_?kAlvFPX86>1q0H4dB~qTA9kP{zGsqu_bAXnU-`blJ$W?FKL+?z+GfeDRmSW! z{vpxv-y+?gz2qXK|7hg?pNDKa)?LHVyz3mxsAz7@jP?iFUsmsC$zHZU$bA_V>X6iZ z|I|}-lz+{Q)_;E6Z9-0+@~^+h^8Fv}_$;Y>jyp3KTK}%{dH%O~-SSh)=eUjg7d+*! z>!$Iat?jn*7wNbpbAU}ZtNejF&Le+`@~_d~%X09Qf2{7S&g|`m9Edf1ZdL!jYz)J* zD*TUq?Xi8{Yn1c=xoyMo@?LTjEQc zG}s7|O6L|2#{J=g&6T%Uckv_7tucD55qKZbzfCMacwmyIpPfJ02y&Xf4ZUuibQi>) zz`~3mt-mkx5`GWhpuEIAy9(if=^9_~81fJF(fp_GA8Z8Azij;nPs1Hp0DUb9@2^IA z0!fW8cNg*k9IEjZ?ig$Y?;^ak*TEwQ4@^S;BuxKVqz@eQiFMCCfbc*Y2wdDV`0xsj zP<=7~N~8yzu5|wL!A3BO<$ElCFK};X8(!j&=Ub#7_D>#$JODndS2QQ|`2EFl^-J0> zc{18=NjrsE&EoelU5W4Y>1dA^4GQtrP4Ijp<{Lo^7R*Qbm`~63#P0(x*%5L59sLE^ z1y_M~TVsCtw?QGMq5mc2zw+NBe1EtDqxxHSzZT^ST#iqp_~+xoJvyFB9BSRu-{Bk&fbydM zE0I5$&m?{G$))i%XnJ(Ndq?~G^7#FNhe1nvrVg?K?nC(K{O1~!hs2-nj`9N%d6dUS zD9>*Y9(WS|GxMAJ2h`&zRH8ltuW5NP{S@*Oogck%g3WJc3Ca`r0`e5U+3X-AnApMA z|Hj3GjNn&w4;~m~1UKM3A@T2=gZcoh?_s^8+3TO@>{+hYUi2{cfEe5L=J%MTC1@jz~8xC0yyGaw6~I373{(f-?1z5jOdGQ3Y$NAw?6kEw@7Oq2SLj`rV@Roeg2 z{uT4_V{^;*ZnxiS*5m(E_un2d#O5b|?&vU!?AL9+%<=>6w@lR^5Wg4KQ-1tj+y%+*<<+DcluWCM>SI2~$K>6`|Q9o9G++h4P`xTxmIUm5&`(Mw& zK*Rr}!n|^+oo9BQw(TJ69ZlZ<%KEpy`(K~a{b8JcX8XI1t_S7svHfyKGwk1I%h9Q^ z^>vi;2QZ#CHb1WNC$_WxUFA0@Khvp{pHlu*y@!?lrz(H{7~fO=%ou;R@>lZwD@`}8 z{3VIfbQdVURm)O-M)_xtDdjIx{!6n;`B~)$G5!+e?-45pU-^f`^1od9m&DS|DSxk6 zJS&yIe~h13eveH{^SMg-aic;RG^c-fV7x!V`H-a2O@m5zqQx2 zQuoQ7Fdr95KacdI;U^=%UXP~yy)w?`x6q}h5gfOV^`Gk0(+G~x_=9deu}{6HIUVlN z_2M(tJ=wjd5uC5?iO$&54_q6|-!iyI(|-r)OMfiep=XEzvGVu~?$P{* z(m$r3%}_p0uO|9?gqQUKx>rGdvfrLGoQihg9T=16JSne;ye|IL`Zqm$;$qI8W;o<6 z;WGH`f)rjvkx!}`c7PA-9nJ3B{&PjS`gb1WCy%qUt$zo;BK2?MV*l?1{9Z}VKc~_N zR;#>{XIC1*5~MHge9qSVkv5N&x?dafpNP4irRgzzdZW_tm&V**()`f>_?Jul|3k|+ zIJ43SGO_f}iqR_|PsSzY${0PY!Mb~=RN`LYO7ld_{e>9q;P*x66R$&gN9knD{l1v{ zl_<}s{~YogrQg8si_*(cK0;>~RfgE9i_LH0xJn~_yInFT4+B0W$&tJ|1YimKhghjZ;AB(R1b(XnI`*xqqP55oJWhD&+_|3 z+dli9tzJh$^U-z}%QwsIyp^N;wc20dzK^8xPmk?e8KwMdj?(y5-^2D=pl71-I~Q1v ztNvTgu>Ld3Pbq)7_6ylhn5z8r;?npb?(pG#2SX9D&_C6TJ!n&*pRba`SE={Ipwz>ZT&NzyOh7NmIL{BD?g{@=E?*-o|Vc^-=V)( z=P~ojU#jJA&bQ^cO8Kia|Kt~xpU`p+p0WMcHOgO~QK1ercWQg~=jGbRIT}%Lh#Q8G zd#3u886T%QVE-Aw@o~2EQ0(6vS{xr|U>9(FoPb@x@o^eyq-SM1*dI6iKK zn}GZKa$SZR0msXY4M`*5c-g?OfaB#f@+RPTIolEW12|rGVHa?`Y{n;X9!;`1UUoVS zH3E*8UDyR2FZ-|yI9@IwE&<2O3B)DfcsU8b0*;q6$eV!UWe;`%$IB*x-ve;GoPk}y z@v=c#2smDLkd}bsWfyS?I9@JbFQI_r8t6DfksoJ}&Hn8`{|X72sDuc?I|_0+E;8Usr%#um|R! zeb9O2MbP~f^v^&CunQ>9G~yOi%q#5=T#ojD-$F6hqI{xso7+n1ZU`UkKkqcH)cw63 zO6dugTK|p6t6=HrN>2j~blcwczq1G7{RQOI5{ciNj=Td$%u)AQa0hNdena>D;12vv zX>%aL0#ZMv|DKrx`2e!tRcJQ}c>wEsSTAYr?{qAC{?-cQPabcf{jP|9K|j;9{wwo= zvi_ajYeWcX@0{V-&jDH>?{@~YxA%w;ucG{dT`-@$a6||!bsIT{HaB%d2)WN&Xa`{h zUwmfEH*wc+BM4NU=5#E40PEmxZf8_ote z&#Puch^J8>B`>L3lsE8`rqBGfkl)`P@>nrEL{`(!-Z9(=Ixe!|6E~y$ft$7b0@wvI zAMUR}elAD)z^h&C@5^65+z7_$?@KNlj{QW#&7BQ4KBga5t}_2BmB;$|%hYB2O8;~1 zAlu*I{#wz0$**j07QLtQKX2IniFPv$P3pzC8t<{3pSh+~f4HFW^L|OT!%;n&>^sAY z{RGjY$-Xzdshk{Okva4owQ~><5Sr zO%Cqt2Z#<$;oafIet_uE@v$EuIy5e5_5(zRCI_0| z8PTEfk$?6BM29AW`1sus9U2cbzcZpklZJnOXGDj_1I_P>=+G1pKl=fqLz4&1??$Lc z1OMy?hz^a5-xq&aKWV;jLd|1RKPv`n{nYcTWPK+8cvJn%>pBhVCCzyKtf{f}*?ZZx zyG*|?=V1p}e!PCl8jllO=W)iBuJbstbsi_S&f{R6hxJp|be!0_juTtgaboK_PHbJr ziLL86v2`6MwyxvE)^(iNx{ecD*KuO&I!pD(sUB`*7>o~D>9VfP~?se9y)4oEw7|%4Ws0l%RuWY`?2nHho|2*_Rru7T4RQ-Fm zU_T7N{l#W=4K@qbm?N&R-&eC7H1Ne%Hhf}bjS<|`3Ewx&$0cq94Scn_^n3BfV*7mw zKBzH*ZznY?S%2=ltHys9;s;JbdNQA99tI7xdegcm9zp!TT}NBGa5whD0Q+>u{EqnV z)i1;)&<~gXK=vv8Uf^e>AMGz>5x;!P@;}uWfw|MtZhP#12NIyge+qF69MCc!n(Wjs z#3P!1`uZ9p*bw>Ww^2;_ilvzfLjrtq?fuA`3L4gKGF2HItueYi&|i;*Du5i zi#5L=A-_ORl(*z1HM5@)@L|2AX-=Fmt6cre-)}?Y_p#q;x!aC4|G>?&?e9x>!F`j!8Q0nH0m2Jbw*@^6 z8Nf}DtU`X@$NT$EMTozl ze1c0`n#K=71L1cTAD$4&8mBmV5Ca0lj~zbWUFXCV&(_g@;=1*tyBANCU$-oSmKz_!o9e+}}7-zGR< zIOr3a_BVcCU;4hxL4GBC&;jxTwjNsQo`?MTZiqSEMgICVy+7-#pNZsa^UKxG-O!(w zCm1&-G-do=3k@^2pX@j0x?{cv;QW$Tjqw~n{fiXr0`?odD!2peHwLf^_+3uJub?>o z?}hLH`;k7*N)oUi*;tA3C%}GW0eKOyADQWi@guMABni~0Q-?yxCz*gbbDa@3a}rUM_vW&M;72$zEQ^9l)=E-{}H=iy%H< z!1<-%KEwxbeyQ;>$P3{4Rmn-1j{*4LZ3!Qmw{-gEFXifIkK3%jOf&m^mwsj~{#ZY2 zFi#?zaC29Cmb-3Of-f_^`65@-|P{eoQ}=W9B9;r*;fzZ{V1 zZx3`M?1Hl-JZNv|$PnRvanJ?Of(Ld&_`PMm0{v`Y)I5X-{lD0I6Yw~SYi+m*n?Gvg{O_qcNA8&zZu0&2-v4?2=eGT-t4>u{*Lu#W zQ`I%2a1)6CX5crgp@#y>{sn&;ZUUXhX>J6+tbq;$w9@nNN8u)HzZ=iPpM#q~=XK`b zcP&PF2#9ZPLiyn)>_L1t{Ek}8Qvf=DGXsAHZo&)5Zw~&lI?S7ZP4xU`lpk&aozLlp ze-duO%ZQ(W-(8RL1AfHM!9M{vq4N~PhkSJ|LHU8(@P2a(@&h+v!jJf9ALmkp1H>=Q zGf^J633sA?-H7j6hOz)p!rqjj=iw%(^EcslG@v}dgNSc#MS0*RWD(yDf7t~n4?yQv zWZ+N1P51=j=ir}&oA46in{Je6Im!bhP#!n@6L1scs$hro&(+Vb`VGyGvPI`(g&%}| z^2l_fd0T^eBKONybw8e;m%E&6GQ#wK^?a=2bFgM`I{)c8Sbv9tipx}gag-Ojnw+OH zv99nu6=z4`c`9ZAXM`kBz69zYXQ>$e!t+$h))bznl4~zKPh}MT7RY-!PbGa(;dv@) zgxk+kajY*qPbJ$^c%I5+cj0*|sm{XlR1Adg&7nPUz6zbCGWvnS^HdyLi_TL4v$LP4 zGT|vaPbIgx@H~|=)Q|l<6$AOVpQkc;N#S`a&W(lVsiZd*o~JUp;lJ)Y6>i?CoAg_` zPkdR19IMq6K;a&t1h?+FA{%Tj`0UKLC#;vy!%A(BBa6ksd*CK2edem*K@%y z;3mlVD~_@g4Z_v1*AhQ6Pey!T2A(Is8*T#WtCZgq+ytes9*grj-h;dXa{kIF+yps) zWfE=z>93UEvQrQrAo-p{esXXVM1P%xn;_?{jLt%OfRg9XCnw-0h`#EajrsvZU(LZy z5PdauD)I}6zB&mvLFuc=f9AcYFF^EFa}LT6bW(kx{G)IaM1Re}O%VNc5^lmAsxRo1 zsnhU2U_G8E{nBwd>K73GHVZf5!k58YP=3)#i}l%mR$oOIEV!iK%DwS-yN{~Bu2`)f zbDyUBZwd7CdH5&#LH|9-Kguupqw}J~O?TmMLhC?!RA1tk{v`gNl73(d{{ZBf-G2%E z5|{1+;Gg#WJAUq;66Ze5$K06TiQj|g?fDH9`QO0%?ct;^=4|=7tSJ6)k)QgXyo>I= zD6c(#e?j{8@Q08;yZVWPiFoO8d6=WYJ2cz-H+Bzz{G zr~Oe9e;dN>@smaV1nSowo+=8zp~!#Z`GxO$FWx8bqWgytKWBUYUX<6KKN&CbjK~A= zODC4^$ENO)8{V+*Kgsd_ljq;S6AO;)KQaHH^@r0qA-T)`6F#rIW#m)4u@;*{Ip9JhwT|bob zM$Twa{wUL@osJHLOZFKLG5rAV7tOp+@2^NPeUkU3m~%CKgz0|~`nxqf&GdIwar(Ru zdmq#1@V;Kz2XYtF+q(<%lVSRoWdFwLI{p1j|K3M*e3Rch%JecWm;7GA0jA%?`%jE# zbh}J2eK*&S(cn~+SDD^-9y%Z{`A#gSeE2kEj()G~7dp)J@g0TqH<><1>Y3|d>N~We zdMMH2-kARbosOI9`9J$U?b368bJX*bmgw$bWWT5FTQIkQ`yTmyqo1FIvcW!=zHO$$ zB>e7&FdrD1sqkkCza08Q8h$|Rv$>mR8U)&&O7<4#T?hw6enxl9Gzjv&Zfa;I&M};6 zIOc$Vy!Uv+Jv>w4gP1RwI8Q3whxY?=-d6N-JP(9ne{t``{LnK~!EqzTJMuFLKjC%o zOJfec&%bJ>!qoS`$bTA;^8+T~CQN+~ycg*KzsB>aJ(z#IfNcOL*BCh5oCdlF@l#5>DR-lRNvwOoMP6+p{wLGx5FtOydTW zm+H@*L3;puhEU(PK!3X(^#fR6)BZ`g3FSZ3{`4KFUtk5=gVK+VqP>87IQ_C~P+ow} zC$;AXZ&Me&mM*!kc#pB{X!`F5F4hCWd7{=I-md>BZ49)5uOuN&hb1vf$ZZxkJc zApO_%6Vxvt{nznRgaOij9Y2E~kpAm<9ezOiuj4TMfb`$8pX2?2^xquZ1nIwyUm`vr z{Wtw9lpm1(oBB2K3!KgUH~kxg1JZx9Z=imGx9h*u2k3gc{;Na({`dde{wvQ*{}sRV zU-3)-6~FXf@k{>|zw}@6OaB%BKihvtPJW^JX#My6$8F{Oh5nO^!p|u3f2}BeH>Yo!ryGRI4Tb3~E%M8GyXkXq{s>Uc z>8H%o41)1o;qx0%etUi&eY4OX#QV)_a6U@&X$rrqr7bXbtDdsyzkk>IFNs~c3_07~bR;Z{DbLy&LebBi?>o0SdUaYT~OfNn+pn~ag&N-d2mARzwoUI&aWstjc z&N-d2RaSJ)RyuKvPvw$cNI=WI>-3(wg~p0gDV7M`<}#kpI=_vxH-I%6vxEj(wd zECw3qPsVQXIa~kPxmy3~Ia>efxmnV0+yq(AaKKHF^$aK61X<5;ksFZp3^&{aS90I27$-g+kC=4U4u1hFUQ5JnJtvKxv& zLF~z8m`D&hslH%uOv6@82;em|Kc9e`Aoj7zpWw_VKDO|9&r|z$9Ib!T{LlH9BmFyb;?eoH%nPM|-^%9%rT$ddzs26+ z#$1sg^F;^diUgT2mSL_)kols4xgtU4izeoZ1ZiI<=8A-yP~SA)O~FkNdq)a$M}o{3 zGnhLPWWJc&h4~sF^F<|~ZS%z(+yt2~W-)gp$b4}Wb4P;A7e_I7B*=Vm6mv%cx{R`S zjAHIckojT?b47y87hS)={1lM+qKUa8f%FC{pMkj|LFS7F=8A;YBv=vb9VX_Ag#Vo{ zqCSpt?JuIudF_od(!bMlwSJu8-zPSo#(2DLy730@`^}W<{tvT{y5#=G`P#pa?a#AV ze{m+X-ptXByLexE=DNahF_XuO&}Cen%l2pU7fzLg=;hD3bUxkuoulaGW$!DboBU$O z)0*zwq79NVPG2la$HDRE6s05cl%IIHepoMaa{N~~oz%zl{GpxclN?{@E~fvjh`xd8 z-77eKE|;6>Cg(?f|H{YoX+9la&U5xN{YRW1GoWgG`)-|%(1)1bFwE)Cbt+1V=^;+XS#yD+ zj4=J-2G(N#sPA_^phWZMlKBTo3-W0AC1| z@$;o%MVb4KtVjI?>%HzpNBpF3(;UXVryk#9*Bc*iDxB}1h5bD;$D#YvMd5esDh$66 z`LV|*{n+k54jsVmr)`IJ|65Q~?EaIGfAX8x)WdF2uk>Mi`13{iIdoRx^Akn>L6n#F zqe>c=V}Fs|FY-#lUoComH{w&6`*ze9kVClLp3sHz(|+IRaJ|AZ@Eb{s?lXJQ9WejGQ#K2kDh)EXLi*nFZ^P#{V~8qvJVG)%`vVLA}6s{>T?wG2U*({-KNK zDExkgVVJA1KDU03g7nRznilN(?!1)LR#4}LicH{sc8#6Jz|jhEp3 z3c!!&iT@dw&QWMbeDbH@CxkfMxe;jrU-~~fJO?-7q09956z)yPA23>}{f^CeKk$X= z+CK_6;c+Mgl)hsN!hvZie*adyA9!lL_D{e~xOTtxyWGexAp2KL&m4pBcbbpP!Tuw- z389aoyx7m-?n8cn14G(B2{%FgUeZ$7OT2RwT<2oG@CmH{`cOXLB-C#P;S+EZWIj1+ zA^*VSL4;$z>*z=Rfh%U~_@i(W9(@Vzbv42lAspC+{E)wFA=(Q#r5){kI(~m`0PhEy zAP*?LGC#tB{Tp?A3qOCd$X|>0waY6L^26@`^Zm!D--D9p8xfz<7yrs4|DVv0C5-Oa zeqZ3U7E|MBk}CdvKmdtUq+=D(*oNA-Bj^7nnz|DC7IFwz@%T=O_6 z5+4Z1o&;Fnot$np>+`uQTMAyD;CVr+OzWj(9BLm&&e?Me`#6f0%`pnb5&ZUX6g|0c z9Od3sIF6#n7LFt533CklIC8^pA4j9`+sDylY2i2;Eh!pD2)BqCEC-ls&$198FXejw9Fn!g1t&55}f#9A%NdeH>*GZXZYHDTU+6 zJg;yZO`?A6i02)SJ3^xbHAfw z@eh>w9Q%Gp=Q&FZ`+mpV`v8QF>62rdV(N(*|uC7gg(=q4j(g&R@3+rC zqCEfDeI@jRIn$@+ALiUPl?JAT1$UW$yvc&;PX3*%RMGic50vWh>wb^k$0zdVeLSBq zKC1Ofk&9nv{WrT<%PR-J*L+r|W8SCjNlTgT;@_c4G2O}ZD|lX#V>+oBbU7PYZ~cf) z$EC@}13%{Uf35Qg@v5$?`FF;gziC!}w9@XvG&h#H~e=~XB<7WDL?k7T*`4$)ZA8yl_BKNuX zeY@{y^BY%IN6dW?6B%5746GW+DH#{*p>GSnqx<3W3jL>kxX}Mz=*u)8l4rgN6NI?w z{{Eu)7k|7kJULVEzmaE5XQ97(SE0Y*mBQz*TvQl7@6y8X)kX2QZYqp_2Mn3?`|k4o zO-1qdB7d2ikAx2uJzrB)zWTowzTZN5>HK*~|A(j_ve$@T=0oB?hvqN8ITC-})rINx z!JplGB)kUYwcFb=D37E?_f)pf-%*smTZ_um@p@tWJBsS}Gp7`WiykTOj39r8tv~F7 z-)#{0E=JDU@gyX^0;K>q0ZjqDeHO^;hm zHH_l(r)B)V%=^ye9P2s!K=HmOlj+~(^fSB;QNi@D^S&x0+@dHV59fVE=U@2!0gj(5 z+F$;Rq|fd`H3W-NN+#qV#dhn7Yara=Y_>JvY;%oKE%<-4Fdt z*H2+fIXmS3^Tx+-JeqyO>4$x4*$l(W>yoK?di?P)Gvw~Y{B4mj-`K$E?k&~%$`tL- zs^D|QrG2Jx{7R;~-*PI7$k*?H|G2*fd(`j@BlX5~<4LmrEj-@H!k_#7bmKI#=OaA2 zbB0m&-RZ`@n-P93*0(R8VMGU~8%rUtDSQ(CEW%C1&s_t1+pZag>!;I|J<q>6s%laL(6s(h3fe}SG)-+=d% z(FN;6R37t2Jdg6!p}y$%adPnI-kNTF4fRR>)J;h5Z_|x$vEOww+5>*8Nr#)aAU`PY zIO^A|f;|-eQMBI$T>qnKJdg5xo5E3E$F0Z@@~iBb$X^zIl34>|G#p7BUw2aoNoLX42|4$WxhV$m~q+>e`;d7@kiVG;^ZHu8x`ged78^j zSAC!U$heSu{D$wmb~OF8tzAFjuF~W3cHHWWW2W>|ssGG9vkk&*)W6Q1{@Z(ep+8em z=-*Woz8~RKUy{a(cNfP0JqE5_U%RU4`P{z3_}3PNccVO#Hr+2N^8cpj`A;1#jDNh% z{y@*)Rb>Ai{8nN7GtWBGJ{142j~9k#ik=@u`6-@U(~I7Ba*_YpXkq;K7KI-w3cskR zKD z8Jh~9-?p^SpDudehl>1n7x~XEDsQ&v{li85`>LY+j2%Y*J^M)ieF6N-UcUKAKXc8I z@GV8}mjOD59n<*U|G}?%pufIX!o~hc<1@Pey!u2v?|rB7>3C&U!T5B3bCzKrpV^rh z!xcyJJ8|-CC&SPd9?=(KkX3aM2>mZ%;dkB%{o+jQH3eK0j_+g3z61RP$j*ZPhx0VtCejDK0zW;pOYT@}b1Fdg+l{^$aH4+-1^y`J`yMt=^M8i= zBY!*mzzu)F`{B>FqJDvsVb3yKP=6QU{lJCq!+sz5b1Sgl7g&b!<;cGre&7P6=WNCA z`8x4^F+k^A((my)z772rn1%MF_RW&y|e?Z>_W~2VI$Dlq=!ux=Bq))%k z>pTU03s?_7{XXx+sYo9<3+d7Gj`PsJfF%p@KGb(=2KEaBUq*dWd$>j6F^#%NO5&O~CP2eo#&n_=3IKI$cJOSz1>~-@IpVE`? zhtPj0d=hCB7I1w?_yV*qg}Yux{{i0aa`Jpu=KbIJ_T2B=?MG#cUFy-+ucG`99r-@a zS>LAU=W)Vv7yD5L`U71{`8)ul!KwW6=lbqt$|>`C;V&<;{lz)AaNU0A4LW{?>7qBB zSEcFZY@NQzFZeRsU*x?GrXS|{zxzy`KGZ06y;Kz6$@FhqpL+IKcETN&5UdfLW&ZOMaMsi0M0o&iR>OdiT=8e7-8_6wz}`UwOUG ze~RnjFw^hj{2Oc!nq>MFlFn{j&u=n)J-3(GV+Oc^$!>2Vn-hI+CH5w= zIoa(^WOK6Ho5<#5w>Oc^$!>2Vo0Hw%L^db8y@_m2vVKGMCW77GL^db8y@_m21@$fUED-?PHvPcLB1Vjf%gt!aGZ6Arbw$>N2d!bt zwODL}FHd(DEj# zm}pOUsd>eG^Pfmhy&M@aueiYM^u*$RPw>AeFU`q*bK_?{ztNTfw|U-M}pg}n291-v3`%&LZhw>gney82`d~AM8eI(U7nCtQP37E zlffWS=bMG}sfT)pqt=n)6sMOA^;xmPCnd#&y3uz8Vpdg`iuX^w!@PpJ!aq~vN4h0? z&yg&4S}}CEf3}16M3OPoR6(6g_0Lu-o(P0J3Dn(DWBikywjiSym@R(GJJ=BpBm$lw z#**)!&g(l3rhl%|j?|NRg|U0u0#!@%&HrTric4K!+6M9eGJ!zZI2jOI|aJ@BGq3mnW zkLP0ju;fgISCJlt8~-s^Vc;jQFWidw@Dr}-MED%od#;_U@Jubj>tIhAnXB+SOk{J= zXPh(7R1o`G?vHpsu*svJPvLoj*taI&CWw8@c^$qb0K~pE3O6D1tf9`^oOn0#3y6Jb zGCvpVw{wldm~c{lb7!8Z@YTj+)OvMl+nEMo?{;0jEXq%K4dGP&)O3Uc3vmt)>1M94 zqrAZWg|LU+2m4dtOohK7eF`s|oNEwH?9}T`>4(lV2#@WAJq`6Y`fKDDcmvvI%!N%+B)I=#_(XBvdke`Ej2@B?RY`eiTTeZX_b54~TmQPc;yCu`0$2unD= zgug)f!TP*eX)3&l^4jfJ-H1>2yYxa+;iMIX;UD<3PG8b{4e7bD$SVF4d_N>^y895H z%A2l2dcbjK6o$Wo{8P9*a|V}J{6mlG_c_PV{=jwYcYGK94VX*cD^Y&GGgqN(P`^)} zUx@UKH!vT#f389!`%_u;H{i$U-xN3X75ITJet-H)NDsIk`KRY4y{!nR{v`hWx9aE3 zO{h=6jSXHD=KKNj3z(N`e{}Pi20_kmGrorL1S~^1m4EWv=&!&pxINR~#QTB!xV~L4 zq5lDIcMV})OPAc=_~0Y;cKhG~-e0DE|LCE#7#x}cjj>X;&Wn6rcd(zpyK^yZx!u7TFCKxi}o>9GJRLk{x1j9 zPw3Wm895(ysl?x|>0&QyXL|9utS+V(pYyeW>EGu4U(PMs{^(|U@xCxN<t~!iG5h zNYQ?%6w{aU{x7l9UBz@c_f_aHC#!1?+wX*aHPb)-qAst{_b~mC?9ck1Q&C2kemA#c zhWBTsnf@u+kM=%oU&}E4Sx#T7QYHqVzX|$IlA;MSWF5hPe ze6&iz>hCs8rf)i3w^QySr=nCa-SsTT zZ_sq%FHza2&-*Z!a{Q+`efe&no#~Gi=w zx3sUcBlqv!MfEJ>A`dPg70IlTE%qnCrO4&R_O1 zf9ZeAz`UY#Dh;zo&vRtl%02i&9mT==%kSP*)Sozo;kOFu!biTpN%IToe-4hHjugh1 z@&D^f3+Zl-|7H<=NZ$Ky=10T2-=~>=6Vo%>8Wm+9(~sTA>F?6?4AZZz)^w4#GCmL8 z$?@0g`Z>VyGxIb(>(qK&mg)B_DWo6Ze*A8JugK3)?tj;<;`g$=J;d}|neIM8w|9=| z_j0|tU($S;zPq67qHpSSgl=&Ec=cMAs~jIwOm+3|;rD(~rz7&{0jW2RFa7lmC)YEN zPdBIYg&8^>;TImJ@8^6<-5H7Oh2JX z=Tqoerr*Q$DfCyFp5^p~KFRdB)X$i%pK09x$E3VmKPJ=v$n`Asu$1ZNoLQJ3C)1zi zdY1QYVET32PKN1sx(eTWfayz$@-xBo7mM<9nCWwi z>eFDpazE!YBmIi$({%Tmva>w*+Yh*oYG2_2@9uZ1Cuo0TD^^&`_<0=FVPwj%->Gkw z0r&hPLbKA_HPh5f~GIsCx=R9-xvO3qT4!1LsHZbw?c zL!7^8be2JQ72z3digd*BJm93~S7X1;m3SVQz6tABH)4LhYnB3?Kb1v#=^e8axUnB4DYA*Dcgqk0bf8kwSUx$`U4KK-~Al&2aJQSQhLz?2nR|&0eyK7&J+3I zY=y6I`q2==ft=KT1o;8(#FlwC%4gh-@&I4w`0g;$2mTFyD*pt^PS}e5a%K|!>FcO( zU>W)&W;P*H` zjm}eZV=NH9c^K)PjrPA1a|}bH09{zE{pFGGkfPLG=V>vP#VoI&r|pT!c*{1!K{3c%eUC4_U5g%}lL;k^^g`2Pv@5{pPKzf7^(Nyz# z$h+7)h26A22a}F;aGt^v#CIdy_~JZ;M-e|u@ed+CaA+6u|2?d?T#oz$H_-F+eIuSH zJc@cuQTWXW2R?!4%^UFi4R{{dcN)rv@a$#CA3*2jrx2cd8uu0~N-!g3@<^)-4m{D7P4>n7-j zUq@X5uOmG-{JEF#e&8g;&%&SiFxm?^g!(scM)RZkNPr3P(EOo{bkppzX8YNd0Jm9!?T1%Wymk3kGdv2i1v5hhWbX| zAROO}^x)53IZxqI=vT(=(7#f6Kkz)#bHnev0_6c7Q0XH-UqF66z9ET7epz}^si0^y^=@Ur5fKCBjBXE8Q z?|TC81Jv&gz@Pmz+7I}~$;j`A(LRr%u7JPb{ciZPk0Km+1oe}J-}ofz3wQ#4^G>wy zr;s1u0Q@QVoloK2KpO2y^_%+`>H|3S7OgKicOV>i>{O(W@#^|2@&eq9`lR+uqwNT^ z-bU+dnQh1~@OIb4IXWcG_fH=0Kh>^ZRjhES2siqWKTI;F&cC*_epST@%U$#kM5+d5tesC7fo`XPp9ub865-H z9Q7AJO(6N<^RJgOedK*QzHysVQS@I7Ii|aRr77(y)G&U`?{#zj-Aq6G13I15<655j znf@xvwd^OBD@uy#3pu`gKc6~A2mFlV3w?y?$6i=iZz7jx7SZqE_}&s7-~6P0?>?p< zW;tlO+1`fv23^Ze*72RsXnB`m`a@iw#wT>SMw$Lzes7A?Kg9G=j-Tap4q^U7*Cf}Q z`HFt;1dSV9a{h+-9^GEAGJT)4%h^s9?@gvZEA4w$lcG<OfO^lAGkhq z+z;n4JyxOP3!V0L)Ag>KbUkF7bv;xt{U$zNJM%YP4-Tex@OvelrA!}Xy~5n5%jIOc zlj}M6Yo~gjpXn2PKD+eiAs&}!b3HpR(eZaN{SenfZmy=Mn4aN!b8>r)FntO4H%TYM z^w|$^yL0*vF};lILHhZArf>M2jxXgMW%~V6KfGRefa&k&^rb&%nf_bR>sX&W#Pl=y zy*WO&{Y9p$%Ch~D{_N*|ec$TKPPg-~_FB!qDlG8JZyh(qzuen2|El4%Gpba_D|zazYc*zpFL9g6YDiQcq|-)xq>_+;1|q zT8|X|^<{2XX{RX1|FZvpJKXC`XBeprVVfvaP`e&Iw#^XWaA7J{uoS$r5^Ot9szJb#*S+2;m z;VvHklFsKj{%R>N$A6aTAD8knzsoW`Cgo!KAxX!<^~`c{g6Uu7^&@HDSDF4%;YV!m z$T9sBOqYK3CeuG#L^pW64oQFG{7+-LPuiXPVHwlU;C?0P&tdxSxc?ZRTxxR!?P2+(5Twl_koJ^PBNfCN0(?=yf z*Ka%1XGr_;dtFS=u{_L_H7m*nrqAa1&Tr~`rkMUT%jeAFT0a_LdL8%I?CrY!WZXui z|M7dX9KX4UugSQY`u#fAXJuTil77#08COY3pXoVHU-W3nVR=`mdyZn6JtB?Be`LIx-H|713vK|JzXXUKyVq@?K7V7RNu6>tFcSY^MJ^%S)ld zjH@m$r*AS}crVj8aegGejQ@E>>Ek#ab-gM25$ET0ruT3@WgOyjZ*^^Ey2ve)>BklE zci|&p&S#GEIiKSX6s05l<#LgW++M<8KEUHy^0S=NDdYF17VCZ}eCH}o$JwIgM?1%N za6O3L;9|PS-3;@0;Zw^vzQ|+YQ>83dvXAOkl#gGOjn#KT=G8iuH8!-*r8VFx}1dW^#R|nO;0E+{g6GIen?04AX_rI?r*c zejxm^S?cFMniYMN<3G&ha&tZ8m_AxWKg{%N_`OoklS~gS)cn)r^xtIq0g)@5e#Nos z^+vXc4wH(y-d7}7mNGq3+^?8^3+KnldXdQF=>nIB<4`}R_WZ}e)vV+Y~`#$x?^4sOC7e(iS;l^TQ${N@j_KN)Vqx@H}o z8Acc|=WKrePNWAs2>T4>Ckr>hCxPA{=-R(#xSfGH?^lfFB3X;e5JM1@-%m2zRDR73h0oH^Q^<6F!dl zcozQXRmdMOiu{|eq49R3Zh-HM!+$sIb8r(HuYi7la5p~uB>V#5If}mr@qv}6png&Q zG~9&0KtIUMLi*PrePB7>Pwz9XMSOtlr}VvN8g2sFKXYWSx(?+5Iv>XSYhW+Lo+pC* z-smXY1o?f9++MUFFmf8w!~5OWBYyz>KBODv&%sTo<@$EtfN)?d+S5s!3T{MxfcI1T z;C-WT69Nd&!0)&T?Mp!Vl>f4uOBJ3&eNla6;3vrMNjh&qeFO6Qky*G2Z=t?2c%L(k za9|V4XZ#rb^H#J!a1qt-r%)fap+11GQ-4Bu`gZt%O5~4zKQes>{J`H)zAVDaV5ldM zy)}dHano=U)Ot7k&JW{#Ks}z%VZX#A+yq+RrtcS0cjEm(1^O$smt!Bw3LHTH&!PNT zxCz%${OPba-c_pLp#F{TjdSo5zC`0?G1~iXv^Vfw>QC@1L-#$U#!^l{4L4yu%4_}v z?RziU7ogvZHP1o(`%n;o*4LFi6Yex{A+-<6AyF(Vp(Jk>0~d54aWeWncgPIh6XN(2r43B~ajBNy#lPSC8J;(b z9)sek>*pLlcLp+yOY&uxZ<)O%YJG1?zFl83wV#P(oBM{60{ND?rNpqyw_F;PPyM+R z;}f#Yjc~hsoBT>ifqcs@EtGH3Z$aY~_lbG=qlAm37_7RtA>(+cHV*%^iM&Go)Q`R1Bi zDBn_N7Rt9Y#;;wzWzQ~@Z=>fF$~UvF)UeC99Qc!6zL~d`70A|u*0MlAYleZ%cnmq$R#4v?kK&?XH(do={KZ z(%_}hOXHUW0^2JqH*MN*I!(7@#2f?niii^y8OZmS1dof z@tkwaU}7NAne0k#2!xX1_@4;*~(;71QWbnxMWj~smT;QoV; z9sJnAj~{&e;3p1#^57E(M-P7L;HM8hdGM)&pE>y1g9i>ibMSKqKY#GqgI_rK+`;F^ zQe#(*?H;>&Y|q#=W7m#dH#Rc1ckKGH8^&%NyJ_s^v0KK{W4DgoHg@~i9b+FF`|#ME zWBbPL8oPV!p0Rt!?i>5a*!^Rfu?NN;9Q)|lLt_t*Ju>#_*#5D{#y&Ro@v+CpJ~8&m zu_wkx$38Xo>9Hrro*H|4>@#DZ9Xl}g%-H9~K0o&C*cZm08+-oCw~gOAe%tu%<9CdI zX#B(DcaHBHzia&N@q5Pa9lvk zU%u_-55I8R3%9?JdEtQ<4t(v}uiYE)Irt>5Y-(%RyvetzbyIv(&!!!l)<^pq z&+T5<($&*j?pwZm#ftN`oZnd4xS(-SV|n9wjk_ARG@f_fu3gJ3&p*Gh@$AOsjVl@( z8V4eQe$z~ZqBtqX+-16&HmvGeH}9Nz;Xtshd*!OO-c?qjs%jAq)Y%^J)Hocqj@sJV z#R+e;VbLPT%+iJa>aew=sb#fmeQWvo+sn`0-X91@6R}9&wgInM-e3+yO>;p-AZ(i9 zMtq~)<>@j_mlF(pqo%di}|ju|SU}agisy((1#}JWbJ9r)T)0WcZ?Fuqipv zV@12YiLS`@R?EAtx1~JZ*t@o+wRu%X*UI&4yV};SUD>c-Tr*R<0Nd1<$Um^n{|rRyY|7h9il%e>i3h_^g4p-gYbD zZ%cUA`Xjxbq{|;@@dsS-fR*sX{E>tw8TALe{y;R2F|$3M40*x>UQg7U1R)epMuU-^ zJ9lgg#zL`RD4%*TpL!&p`gA_^WIpv={;ECsJ^S)|Zq4txH^1k>{GO-t*QWE=-kQJm zw*0lX=dZmZf9=!x>(cq_Zp~kJTmHJ+^Vi*xA4%m$cIQW~&W~J^AGt0+GLj$Jn;*F$ zKXOZcP4-}^v*??d^$59hDnoxlF-{PlbC*I%2z{<{42AIe{UXa4$!^EX_Rzi}jgpSn4px+R~wJ)gQGpW2sC z-IY(>olo7HPu-VKeI%dCwUwzIWvZBJ8cPt&@! z9lgD48ccUjTW?#~*U{Cx9KC-<<9S;e`y;XKo>(BwFpegoBBGg=3b-7q_gzFpe(ji$sDrz{=VdkA&xKnm5nY-P4O!9B>Sk5BWR^3+*@1AB%+i zk$55)2oECp;dZ!N5#~=Mq60ev(ek00rK=)g>%3s%g7XFv7X*AktJ<;D(c0A8Uy}Pq5p712%!(nfKAZTHDm6qZU2Ezb=R#2d8Sc1wzPT_~(`aIFo!=4euLK&*XrtE?uLf#>#Xv1k2h^LM zgjrgGks6N@8q8p^PQ-D3m^Ts~j>LTZA&)nPu~!2uMm{~bSA!mE!q{#DI)H9q9pC{hAOJ*wBrpv0 zha>&X=p>f{9T?|*xbFl4N;3r^4~56^ci9?g($QlHhjTt!>1dzd_NKMy7nT)_TLY+L7skhGI_nG1R-!R|R)3=d zS1-l}^;;wow&GR6h<9)ujUx|AGu)5U-K^yhE-TvTw6cZ;3l=#0h7(qNEpmu{gFiGz z;@)5+ZZ*&lgtAh88F+VjyuG*AwJ46{;wvM`u*+s^rl7X$*P&p@`4xkL*C&i5(r?L9 z47Kw3qH-SweiEnIMKCPQ%JMkmo!1YE7YK$T;e>xpgvN}u-3t1wz9gy=wcGDW;JB}F zqPiY`7T4fUeXTleE*=bcEgD~q@23&9DhAfpE(&m(4WQhM-RhfqQ7!!d3-^+FJTQO7WILfh5J#v!48bS23H~$Z-|C4 zHoP(K;u>!>8Sp`{;($48$Qz7OX9#*QWIf&_T0h{8MZA_T5R4J6iMo6f82ms}TQD%t zM&1tk(@lSf55?D!tGB&-ZC89JqIk$lB4NN|!E2E>Kz}0iCrN)2eyFfb!KmM}$`cBC zTCHFLtOsLwwI>?&^nk;7x=?WZR_e?xu(0Rw-4NI5S%NXAdwp{_cfIJy2DnapSViA1HC-j5soGko5~%FBP5v{ ziB&a{>}@0l)d(RP>0e1)MIV~i0$$VI>G$mP#C!;Y+K5^TdJ^6Tr@A|GtFk2B9@y;bOVzL<2C!i1Wd?z5A<|Q z2DUdU!QQyg43Hk-g>Y{)Jw9Ko(F|3tfJP9lToG#QX=!h3MQAcMK)O6>3f{y}qQOLU z<0;~RW(61;W~E9kS-i{vo>pyAt*Sq9;*IgfwHCF4&WJve6Xn4x(}kktrLoG`Yy!BOk#Bz{+2Pk$HJ(BJx6;_J&Rmsir= zyqK6Om?e5{OC*H)Y>IjM0^a7B#~%u`fD3rS&@~1_o)+kb0q?3v0!n#1#8fia?!o8` zw_E7KJ63xF2w3fpY#R)$v7$(N&9E2Gt{Fxj3!`*+qGuSXflCIquCaRIk9+8EGSO=V zQ37Z#5Vh+)VMwY=tepX0U_(t^UG=i^9pzPy9Zjm&^{C#3i3R2&F)NWI-3d$~Tou8b zIvj?qk2hc(_@J0Y&qp-sR}gt56Sw;!ArCrtB?rj8}co}w8jJe z>|3e+D*oD_WYdZkQcE|YkU+@#n|i(<=dir%K^9m?II++8Y8x1w|_O1GxO!BG6`6n&kdU!wkc6vd<6Dkdrk zY~NCJ3{>=ofO4bS;l@w_%Oi=l-O{20JQNgPV0!>`^_HXxO{)0A>hBs)sL$ttXvAN| zwfp^6Fc^sLumaGpFnd6!1uyVG2S&|WwMY|uEC@LriN(}k3@LPEs1LCOEGeECn4S8I z4g*ch=NW*oZdHGwu|awU<0Q9Y>hBsWJdp6?S)Uba3S;cX)TyC0>hEF~op4*D{w{_h zh#pRV>uY_HWFG|Wa5&)^ib5x`eB0v0d&{>snC086)Wbc!Yunm-@fWj=KqwkP=5_=U zUVle?J=kiGdKS!gwKcqwxOjY3g!Dqin&VxHTXw{ueJ9iaigzayVA5(L#k|y1n#$rD zb*ryk7e&qbthJI^Gu?#tJu<`^*Xq+VFg2t+9OjLa$NcJCI%T=54{6b2Mv~dYX`i@l-KI$ zHg{m~$C7vkV|o~EOU4SEv>F~**R{H)05-qP0p3fkkK01Ur5cs?*#K)w2=77-lIUnOeK3v;fIm~# z;?O@}F)`6w=|=2&zyW^k@l}H*26jp ztE0)@vL*GTjh?SrH_qFIDi`Eqh9U9js9&X|HdOl2DI&4qb)*+Il)_XK2akq{2Gf~g zm}4T4vlt*08VKXP0UvQ?2!Wm*P>ie?xNW#K5HpkL$^+&?m5D|YsL7b6x%G;cNEok7 zG(m7CJmF>x5hMeSjT~7m!_cK72^^q_ewJtpLh5+JZNXtMKkq8A>xAC}?wt%rBC9>& zFvM#o6tcJnb|VOsfLAHiR=5*tIkf!FVd(vySa%$@mtjv2CSiC0H(0{LJ>Y%NfqMdB zoCodci3|{b>PO7-ZV$x= zhgqXE(o9Aq$wIR;(}tCQ9{YFCo}A8+s^BG7Z=k@zB-wV?A6*J*<)3B9IUX~5xi z+Gpt-MF;00Z4*&EL8~9iqAL;{9w4PCN}ZkbJ1BSRR##6qD0p;(d}#A2Vbs}LQwfU- z>?(9a6=Qnc(9qD2s{s;}xCrJ<;8M`^hb$jPKi%qUi3y>>NjrkbAxR1*sMt`0SyNS4 zRa;fBZ;SPr{$ZT=h@Pqo9;v0&MwcGD14lx~`55T}@rx16CLl(1f)%;_;C(x+_xAs)6V-rd!`6}HOmwXU@t-LPX_0vlE@m^Ly8 zqY3g$S|Vl*gGsWa45DYi6w{A9(!@CqbsB>j{Aa+5MuI_%3(dB0>M+@ihoL&v5SHtPmaC$YIUJa*L!|BycWF82llbVqZkDFT?2Nw&xs3_dhIv>Yh+`H~n-@WW0Y zBN+foeGc_Xbpw$obdBU(oLeqy%U(1k5i9A9NHF8f0f? zF{&nKV0dQCv!jxD0!b6%apb0WWdDQ`76PAAb~ewE7Ycb)0JrpL5nB{|MUTJaN zN_=>NYni^5=^}IVy}q^?c6zZ-(w$II?wb$I2Q>rp9cj-E2Qyl_R6F=y&G#ixOcN2a z+_#n0I2FGy+20X&LA?%mLY0#8N_i$rsB$RTLs~Pmov=wg7bXa@c3D1_4m7t>`s`v%b+n^a9ChNT7snEDEEUHx zcGNn=Q7sNhxmHrHm6U5Gm=nmNx4o^u9KAOB;`6u zxlU59mz3)z<$6iEUQ&i#18Wqj_LX#2KgIzY4{=P0)VPV+3l>`|)Re|I-=tQlu7Z(< z95v*CsXl;yMh=qXc&gdNNF2m`q}c>hs+vy*oft$u4h3= zYHO=&$ugzJ4lxc@K5#!;jqxe*2BWKYknAlx{1}YnpuS2^5EvxlXgz^s8WQ#d zm1%CrP)z;zMnch`HB?_K>^e+qULLG(RZxwAHw9o`Q9oAGpq9IO$T~yoW{Lwq9_t@i z=?G)gA_G1cCF7p07km88So0c;CkG)&!d3w78(Qs8`aC;e7w*K=WSEQ>kgNkREqlUU zo<4{*%M*5af*~wKhpiqDS`zDsVP#n(!AFYY5dN29rsP8%3@XZHn4ZR!0oqK)An6rD ziZ_t7QB!DT+g}LF6#g$%oX%mwe>b7$pR1E1v)dd4DT=+8S^XH8g^`K@C0Ga zZP-i>Sl^W+?hORUL5A}|a$sp879@ucR@($QAQ_{6av%+KQ8+M>46C3h8cI1}$?;kg zl%$y|Iksp~h1OYIL-GZV`dV_Rc^WyIX>p4jtH>fw4l-bGrAAePh-S{@*-E02%-YI< zDcM%&STPv;L)KQ*NP;x)ezJptTPKt)rcx9*C|r?#Oc}zKOjVXHL)@6|ITg^)JGxri zHjt?3>b|&bZS#CgF=&Q~SNC9-f;MblVDg-_k$&cAsIqCmf~y^qnC>tZaG@nyo)D}x z=KWTq#RymxR72B4+nQb!osT-Dv#v;j&{OAl-qQB!dY zRznkhO!Jhzf(+J}Ao)}%Muf+!^%GilGs*e`>lW6?R$1D*GZ66v!5lSD1vi9~OyL#t zm1QTo3c*;S?`-Q_)6v<{YpxD7qucKQt5&lnT7=g&d(iTeX7C$rs$11U{RCE1#7l-T&XVBzRCE%>jfzHG z9ZLt4EUk8$;L4F5G~tD@9~Db3&4n?%m$I=BJ9d<7aSdE#;HRI^BBK-4c7o<6WUvU* zMg(Tga54wSj-i1Fh6+?&7@ult97`Nzf2yx`EUr@vqKZYU;f1tJwZw!P(sPqIe$b0u zo1wf#Osu01&~iFY$!W@hj*K54)4jyEwQpE2F>D3z)vgA(5?J<&HDLFKuNAB1Ru=>c zRMU`E)rziFp*DOs`(Qr2#8%=2IFnam|mX9_+ zEK-YOEqKBc4}mAQT9|NP>Jba9f>{)78k2+7u=+$`_w<8@V{O$>EW11C+uA`Zx?Xa* z$XKjgy~)_1cC8y8z+}tik6?mC%MU#sa7Sf5g{{e?(FUyzt`N-C%9VtQ1ug*T&NM8t zCSVDO5m!({v9iO*3N2hJ_i7~;&k>h`B0) zeo6)qJ@ugJhMs%`;$0T$2H0KzgBJ3Jg#`+&QLEYB*l;uvS%h`vh0s^KY^|)WcVN&+3Z@|)@cj-hVl(Oui9F!wven<%9NT!kxbNUzilJ~+dXkM=v84` z)K)Y^(%RYyEdlBoO|ID43wa6=2XW_G+txB)k4UAOsE!1lkA9+@+JK?OUKMR)IU?~K zi)fGxRrwR4pxG9SwU91}RSEEw1X$7l6do+f4_A8nF@R_q1TjapbZjpp9hv5-YU6|1 zj;f3kr=@8sxCc2`p?0t!-PYCAyryldYh6z}2{m4aNYW-V40LoD5nSMc;>3ukS`ULk z3l<1rXFll^D?Yd(5wqgB#AY0T6@=G0v7c? z=&8ik2fWJMNBX>yYd#p8wH1(L8I>_!Mf-I_FyWarB~^+(wwFzbmMPW^OBKyD@YX>H zuyAuv&srFU;*C=gTqe&XNIik&qL*!iM+9IaX~Q8};7-zS0Mcuf@SvQkrXj)O31hAj zPtasROGy@fiqlstG(Fm63)>e~scABkMBXvq&`ww~vE7a)B%ls>d9eoRDybfT&CFD) zJNEcMJ@0g`6T#w)urDP6ALcQ7+D@dp9cqr)QzAx=O4_=F<&jvT zf|Ld}b9haQhE0Dz%<}qXCZoQl=>J37yKcpiCE1_zwY65iLsa&ke@azJ5CnHBmDLD= zl1@Mrfl}F}&Sn7-TuR)M03oWn=i{t-qyFBHGEXwUZI|QjK_K<4>CVa|!aX9~kDp_< zZQC|G%Agy^xp6rO4R!u7xp%c|iTO#H6>P@k)ulzPXH!=;cUaPjm}E6>uFmBNdzC93 zx-W!(S6<*0&mDhCpC-8%Loua4+#QOb%XiU~{$Mj)AxaDXCdO?l(mD3$-2AJ}m7mtS zLveHd=VAR%9Adu9`3d9C219{ucWEAg{kYcKc>5ZsmfmIEb3Jkq_NRN0Kn;4Apc1!W z9>G0_Eo7~$`+Xec0l-V?V>?0VRbkA#lT%APyIWXi3Px@RQHCD3W;l)^$u<527(g76 zksTH%63@4_f@y_b>u+A2Y)T^MZsPsM>%AYtPe6i@Z%)j!M?eazq^d*z?PjG-(Iv1$lD@MMbmyuv}QD>q@SN3xIhJ4aw(clkb`Svxk(efGm2KY zl!!^eB&&-M;yw5tuw`+AJNTijDhsv;wvjrN7YLWg@0}dILKMmfqAxQMWO-jx?Z&dSLw z;g3GHuZf@1%sV9|ldlmQm=!Jo7TI(A1h`OC6(Ad}Co-l>fck;^o)$NP1jqc4vtBPv zf9k|n{C#qy)VBmB~veaasKNtoV2w79I%iPTCxfYe}CtZp=tgYtZ8}OA|gYb(EL1R6_o(bJ7VR++JFhxP*v*a0bSF7<}9z&2xXc|0CM4lO7=(t^}2(|)H;Q;b1#ADz5IRDBF$djul_c%pa@YIiY@%@dj(zIuoVjp*p;db+#N?(qN9q=y&+=>1b<_QTuNjn}&wi{-c}sg(DwzYSRu z9y04M^lvN;l52>9=UCAp7XQ=W1y==DK#i_E+}SX!%%K0e_V*7}(Di+Zoa#{&j-|Yh zXX*7e_gB|;_x2Gr?h0P_l$00_?eASG0Z{1Vb&4nvqr$qjDFVT1a!Ri9MP^CJaR1OV zok2u1_P6_>fIQ|{{z_UeV-=;AF2F4N3^1~%<69iBqRN}Q!-qV_MN}SbFjp~S{`c8% zAH1(2f<0T?-B{V&ez)~%ZI{`DxaID6A1Ouoo2h1A%oS6^DWz1WTzJ}PV>7PXSHp|; z_2F?3^CL2)_5R?2Q26t0$p-NU7FTG}5k{<|H-i)&a)C8r?J)%hn=dqK`5J&I|_ zJmA9(njdVdC z$CsjbiXZw6jHzB6ot~5z7uW5Rj$?t=2a8#CuNueyeD)6(*uDJw_0cjKyjn!)QJ0EL zUJ`B#Bc05UtWBAVTw7yMpQe`EEf73iw2VaN)efCgNBU=yap~ct4ipHOm%Pb z^>yzKY2u%(6r?r7#Am!IldxPB+rDd9QH&>9F}%5OB%6v|EIH$2OsYcJoNW}yhC+&LvTgF1R_SQsxW=$r`6uHXbJ)lFAK;QtD75$wf4~o zy?(nT8-h>)DhZ5>y}x0+MQeCE(VViNDtRm5m~^0sY;8cq;V7l961wjM5?IvKBAMlZ z+`)q)sg;ae6tW1!i}NXE0(2Toq%v?yI|*3VUgU!pYA8s8H9#O`&bhC^`+JBX2M+J{ z)+ZP9#Y%6xE#*THi#xp@FM?b27q~ppi+YisZ+&BPjW_~kh55__;^F#N%P%{Sv4@Ho z)Z>G<>RVPQCX4%_>(gs{(=N0)!`8&!51yohf1<>il}Rm_2}=4bGoxMjLHN2BdIVCM zgVQTC&?NHxceWKGxSL|uQ86;rm!9W%KmS1~hI>9~y;3e}_-6Z^tObNY8xzK4Jb>i} zl}Z6xbJkQ6uLhm$isnDINIMRC1R*as4?ZJrOOXX+V0aj2EMH6I9rr=#1s8+zM1XML zc#(a|K6HyR&It}oy6Z2>YF2!;+}dUdy6Y2^A<479zDr7Jzqj$1HR7ecwZBQG! zw!6I&uXfkg-{Dvy@iZl?Iz^Smv1FIV$4Snh%-i$twaPt|8r=M#No^q$CYNgzq{R)G zi61gOuZ~1#1@c$E1t|tr_Pmd0ac<9Ci?JXqs~dZzI?y)5*l#O(!_%z1+Qsc095QK! zZTnV>Dqc#==_K7flFyT3oGC-P78_)&nk5H+CwEx=3cnsvl!O`Ob21v0RaY?ba_iL} zU2M$Vu{^jvCMwLtZEI(B(jSwjrJm{TxbespAzPNEm!DA{U-4odddZnLgJRH?Sd**5 zt-^BZ7pgndEBPpQnfxwZOX&bew|k|QSjR?74M&V65Ea!8=(7iMRs(dwWP;IUl}Sx6V;Ag;}wAMkI@vWZVfg?k{Hi-XCZJMDQ{ z+V&>qN%RRM9kA|3A-oD?SeFOKgSJ&x@qnpu(>y|h)!JHF^;I*u z>Z|ef$Fqw`F=vKAov@3q8kVcJvCyMsv=MD!OA22+h67PVbM37xI1Q-T?4wtR7>*ac&10v=e zjGNUFIi9sZLw0UWKCc*9L#j3|fOsDln9slynFZ*w;5)G}u zFpa6BYys;xJH4GN1yxux+$K3ibPHBtUbCR`d9{~vs16;9Nv)Yp0ryLSS_8F^ku1b8 zoT<}`mn#3g!`>IxiYSuV8_#u3Wmed8Z1$&p2qBNk_c!`D5kgvWJ(E8T9(GvSs-zAh z9x0919#0N5;wBxs+4JDCwQzOmY9r~9C)d1!)~O~Ni$XF*nJvdnHYr(7WKH7+AtV$5 zgaR2qh{+|>di}7k6|is5&g{cQ0qOo#VxC%}p(u{LJYveoIr43c0PdX@70+dlUYKp& zLM8iI>y>gZ*=PVpD5RCH28+aXi3Y0KS5UUVEyj~Maq>(=lE@FjSiIp0f5BCraJeka zp+wdCDl3p96!g{zR%Y`i<12z&7y8dh^CJNxCNN|Qz9)USC-_CEI+iu-GwuHa*(2pB4dZn ziD-0x_lk>`v`ji)#~Kpa+Us zO<$$qlquuKl{o#UVgxm*hp|F8WBvXzx$dkp>zBXs`Nl}6@s0a7ODV(J)*>4|fARef z|NPw_m;ddXsSm&Y=_N){g`!aC1LbHa4r94Wb|5@;4YsZusMd3hw92Zls2nLm8(Bex zGc3uwOQ@AIBZ)fm9fY*gbDi10AlxWVFrwC1IR5*!&lJ6`95MzqCSR#(mGi=XW{w#@ z=Ol}-ZnL*D`(kdCU8i1-E-DOKkTLB#{nIa&S-|XgG?Y@}l3h z{2@2jvT;;OG84?8zlIuAXvb?T((oDP8|}kB5yid?>^Z#4O8?W@5Ws5;P0fH`9Q>7- zs)mr^7H=wv*1_SC0KF2)Ew4hB1ymY=E2~@f_cpfH-V$pSobLF1cW`c2gPg$y z%EZjSmk82Oxmk$kde(qE3xmp)qv5n;c3l%kGdCHOK^_5ARJ zK3^@%u~g}T;vpghuTuq3Sa%lZS$bc}wzk)XSFg2*Z!7^zxsx(EkW!a@*&Lb)k*;_! zWLCakhNSM1#GTY%<4IM92n^ShdHAF3?B&(VvAI@1xP5pJ)ng9iSJJiLA% zCIgS{aLBkBAo;T3IHgDRRD(#vuvI0UQ1Cqt8byP7TKPGP0tj&I{vIg8)5<5-lPO@` z=kPx>pvX@dWPoTPlc6Gf@V7HnU2OXF;@ra0bjRdo)p}idwfcH(9RZS;KUu7$si-VG zDYCZ)QJ#C!z4hF%0^v|XCaiZC2K=q^8W zCYOHweT+|=I>v!2WtM|n%+i5;9+<5=EyR4Clu%e6rjsWzf--kjiRp^*mm1HAQ?30- zb!<$%bw>-*vogT_71#n*P!h%9Nsjj6sfQ4*e?i)IsIrFr-fvX>5c5ayU~$qw&P6I@ zx~802m_+F_(aNL=ob>#PeF!(GYk&c!GV<*Wyv0lNbb$D)^G|K88qIYsCpUIJ%_g-H zujyvwR7;L$0t9~F9GfCA1@dKl0vJchaSrP@mSq=G*XjjA;)OsFh{4DT;kBQCI0SCi=__2$PAw&aYTB5eJMa z%f>uKdqe926QV2+vjC5La6Y>5f2rPZ&s`p^k5vmbfvg&IWpUQEc&*2mAKj91$rKA= zG@Pe%Spcq+eIKb3Zd%>9U?XLvku8}7(Zc}buOtMhM|L*ui46&IF3<`0AIK#(1G+8X zSHC3*DWz_3xDV@2)4wk8YY#2~)tDT+>DPuXQ`_G&?V5Nq3UCqHQHAt&+gQuAKydil6)*JL8 zVTR(dh?lHbSY84RtC~EW$TFOCWO4V-E*~UyGP$Q>2#f4&ZXeQVB15}0_$TQrg($#V zR2~zNOj;D>&QR}%0lt(@xZ}9MLN=$k^bzSV3VtcS5>EU>dBYhV3Bs@2Ln;-IPGw_o zLC1<~@9_^*lgkPso1@<5?>0Q}&S(tK8z3`~P%OMU1{EA5#ec5Dua1~O^V>aI)q|h7F(@WjXQg?A_ZmwhPwdR)QJG1k% zvkMDz-k>IpEbY)d(04EY8YYV`z#Hdt4ap$mwWT_S* zZpxP!sKAyKi^uakI9lV=O03AEBup>{ljIaEjIq@jr`yvb(X@?^JQcV&EJ{A2nS2AG zk+g6XeA72s$&$f297IY#%)P*#mcY5hmkMT%u1+pl*mNCZJW3cYx&-EH*uX@J|G-)5 zPX~2%aMJpIV)#RYnQS8x%kh1+wIj=aiMo@eMiu?W%ACJZh02{d3zjD)em8OS977^^ zM7z5eX_E9?S`v@m`1RTfQENdn#^k8s&T$rqL2LdQ|DDVp9tVb4E1En}<)QOR4I#_D z7IO1F@ea>*C`{%d@|Ne4Hiq1j;{}JZwKsYOAeu8sI&%OhPF)rQu8Xi=?QndEl=)#2 zb4@s9ilxApN9mP$R8|!2saarwDY`8hGxdw6ll#<^*{LwYgWh@=oZ9mUH%U9axtdh$ zJ?ZOt3La|Y9l~MOQ-sZ0Be3QDJ&h*)+2kjkrY%mjyyN{RJO?GsBNA~_u^8TMx#yL8 z(Xe7DCRtxVCfcLDrx1R7Z#Pp@{JPlx_A3CQN=Q)T^Nun}w2zDg63^SoIVDt;QGpnv zZy`Y3EGPfExg~LXs01LmSF4)EWAtbG)Lk*4wXj$A1TB!*N8uDIfCdU!&n=WxH%kO} z0j{z3q+3n-=4R`;aitA6*)qLQh$6%ZwiGg7t1eL8tGLNNr7FmNcmrhurSAHtGV>|; zF7Mbp#m(W+Q0`|uZts?JTIBuJi@2gTA#osdS|@4%OE0R^9nGOzeh^sAv@^OGLf80W zg4!Wh;9;V5rC65Uz(sH#dA2_234*4O*{|~{Mdanz_0-H415U`ELCew4(8*L6c>_zZ z4FmN*rjRx~DXBSkl&p$7I=a@<{@ta;PIsnDthzAo{dpwt27()^9+=-zl-_hejP_85>#aG?RA<&m_P4L`GrJ8HhOhAVBp(?gihd}j~0AMY%;{z61#{>f@m zrWiwX#_pgL_?^<`K&-t8&Z^5XXPSLkD;mKU|; zN%$mUPwX4l!cP!iXf2z)6`nmrLjI}ldP;qkxvNX0z$&=6u({Y?)fS`c;EA#nEzw|z zSu;Tk5_Ax*PnMQn$ADnN6XO(){+F#y^QFty?sw|?ShhY5d zno1lJ{yRLov(L|68ECg61BoLqxgdcuTk07?66fA}Fq-Go-l;LcqX8EBB zDIB{p+H~+XWj}Hd@BsJz+on^%UUfM%)KUvFcS!v32r5P1&q{FvWCwMH1ashA2Cqg5 zd@gz0wjli<N`?w z|B8>ah|WNzcONCNq}T^vVt!cvzE(RW;p$qJ#0ZRgju~!wE`QT}>xJGjIL;{C8Te!5 zTffgswUC$TM7cNyn|6c5sph^cR1M#4Z-mT`ACDv?F8Uo^W2r>N>ni@7Qi^ohW})8F zD+p}D!sc66+-+~k(ODe9HL_qx#r~#Ykl@Olnk`Nm;IK}AFlyW7h-!d`_%2$P*NuZ7yCPkQr+)pdOtqR1Xq;s)aMi7oksGR6=w!h6nE?_#4lX|>%b@ED zbnxH*bzz|F!XKAVo#79<>oxPeb`eDt!wSMsZBnYN?_v@}eY?S~_-ct148K5MRBs(w z6b9ehXQI0pa2dTK-j2>s{l5~-Hvr1?>{4fXdY+gWV%(bmF3iq#<`z3d%}XEHa za5#f@t>nx@W`f5kN29}$$CtwNtOv*02-kR^khe^9f2XOP6h(7w^$mtPT%9Axq1$#~ zgc-sf)1z{VX2?_LXNOY^6`|$S-e0CvtP6_L-KH#!JuK!YJJXY$Zfkmu7O~5nxu&Uo zrH&2Fw@ky`d}~8sv-y^F>}bBV3gEk;LQcBETW$p%0+H;w$sR27rLg?y<_t-~O%dU! zf15d&FoY5h>(iF*aS@QcJ`iCEtiY&$L?y+9AfN3qmEn18r0og703x$og@;#*!hKS& z)~fj6{3jZj@J3Hle!pjzkp|}IOnqW4TS_Zn_?$+f8n@4AHr^^9tkT&DDcQUI zZ&xqP)U6lW0M<+)W_V`UUQ^=b??Q{hI{6fIiALSq(}`RcC3-fNuY zz$ixomgO{NXu=XE0BjG%YXk(;?m;>poXd$aly5g)`&+mS3)qb;@wf6D?ZP#EGshmp zK^3|i4QkmBL$IPM@~wH3mpKH=Kn%K*tFM;aD-(-=e!2 zeanP(xX(ljSAZz5^lnAkT@W(oguGeMq-%}z`-*K6pw-OTN^-_0JgwlZYk|n^mCJ`K z?|Cvc(dVBwfo=QbZ)V##Ao4Arw_PbiK4}YR((WlT293;p4bN>x^1P1TOMEPH z*~to@vufP33_!u3sVdzv;gGm@(<-@XA*a#mU}I8q z%Wt53U{<+GK}G%5n(Jc$V&U1x@Wp%%y~kb$RIy+Cy>FjQ-grMrpid1nO+B9jyu ztPvyMK5U>|R>iF~8|+~rK?73mMj~Q!L#<&zW>#vgfwT$b;S~_bD>Q1@Y*)bHwYs!W zAw&5!A=3-V;#mzCN0EiZ99JGgf$>0>_qW5SMRx!5v;X)c0wEK z2+AZqQMSEzdv{pjs7?V=2LFrL&B?Brh3;H;sRMU2J3qbT{l)}t}9fl z=hxh><=*HuWCN9!!jY`O9c6O|Q&}i771t*!-%=|YbH0VzCJ7KLg>l};&!U#v8$8*gZ~ztx1hRG_rXRraBgtoy%n#S60RB)L)ZR;*v(x*DvLY58wxp)Avq2Sn$!k_0 zO04d+sFNCej|f&z!m8jT4sc_Ky^>>QY*nl-C8SmgQJQ;%?|=891Rd-ra#-1M|EBN} zvRG-KKfy+>f}Hf&5H^t>$A0AZS-nXlr$=GK5Mm|ID?~nnm_Q3GP*Xk>x~4Gf1(`VQ zLW`i=q0cVUM=EEd25Ic!LMXw)542@IAb*$DM8_>2IqTWrgXAJNo`uk3mBL^YEl`XD zmwm&RV%0*_m){pU8H?85uk3EqBI%v|2q7Tw#9qn60Y-pYAbEH-1 z8)`oBwzuMThFm#tL-tFsfjTXw!*TWI)uDJ#v)oR=K!j6{B5}P1ARH6+cFeHg%yT6LPWC9kk*UnCuvmyiR5at2ekBU8>d)=D;g( zJjR2w1xRo5A#Rf^lX3~uken= z^+@>|kv-?!+?psGvzGa8dow(L(QCt-^tmkKLep;)5jX28pA4PRa87u+8t4RqQDt&u z+rC-T7-v)~2?FT=zgoVi+a$qW`>@i znG+A-i0omW<%^BytH<{h*ted(wO< zB_|`KWD^5K$qq7AGsETuhgcjw&N0MzI#277w}hB+MX2-Bfa(>QW`8qBAl{5DLs^Yx zwO5EptvhJlwF1A565t^moTnCEKHZ!&M$g#{i=Opo`UinFj9mS3FqvG+Ehh7PKzC|` zHT+jxM?JPTsiCUYx!hW=VNGH@ZYn(Nw&#R$8>!v}ZBTkg9T~o^dN&GYtOa%T zuBL1H#Af7c76=s*^NuKmAU|-d`)`kQbN2ar6TivXbwj>WSyKOHY_2mXkpruxHVAX~ z6?-C%km!Cm%r*naEI+FSX^!|_jr^a0mQH#_v1t}zysIi=TU)@ugVr7h!Zw!K*LmH2 z?Va_G<~eIU#hc{d;rn-0zU$3J0Ly4w3uUW;R6NT$#=FX7;0;uD=%EH55hZ;%z}ON(@u#Xjb#H7OsM=@SW<{$> z4{R_n##5AxzR932l}JglQ?>q%7g9 z7_=5OOaY+F%|rYeCD7cMMu#VG(UD1D&y}I9zhPmKdf1DSDfiwy*7`iMz$8b>n- zLT$Q9<0*Wpg+g71-!(8nJ@EWKQfG?`u^&I+@zN|t=Cg|qiESg)YY!>YQF@4oPR$Ef zt8hdqugOA#)>+yf%+t(tVWzXNFtZdJQX=~b%D?Y|A7i|}k!$$o--f?m=6~Nj|M2hM z{QCPB-xwvYXs?8+Z5Xt0m{x=Ivqhm=g*6lHZh4Fw#=zM_NtdhF%-J^X`KJLbt?N=X z-m}apcL@v6{-cf{f(8XfnI_(45=NjA+ePRc7zNVW`gerxJ#n&D7CkSovyRqQS7s+0 z@0RlW{Ai7Mz{^FLsr6IKmDa`~uGP|#BQ6!x?M@60aThEjy((rnM@vmOJk$ zOx&SnZG|?T)}x?$mu!x|%OBOd?B?k2Qp)BmWsw1dr-Qu)rCy=W7Y@k)IOEMA_TyG_ zI_I4qYjexwuQ&l{d=hr(-)?lkVCG>{z@Hq zFo}edz)TXs(O4b6v z0aT(Ruo-fx?8uEmegV5CU`8UUt5KIn4?Z7+)^vAS_h6{VpTxe6!ajSsE-L0{0c-op zgR0h`o`QHquWQ^X!9BtsOqEPW;p8x#5*>2Q7F`yUR5%vrMCep%LV;0bzE|pu!7zeB z4on_BYJb+C7)SqkMYV;f2vwo2uC(Y}8s_GQggZV|TC!AtOni1E6N1DlboaM?+S2+F zjAPH*->z!0@ffNN7y&cMSjlSJxblYNGH`Cno}^ikU8=P@sYmv;w}19lF0GaG;Z9oe!#R0(o`3^J%D52ooHlaVV;NG#@?`Tc@&@2UF5U)nNv1Rt}7P{ zglP8GsK2Vgn^Dpta7fxko85Buu9qm^T~?aSm>O8A3nx&tdN!-c%AXv)2JIppF$!Eu z6(;5AmX?T_l2E6|KMHjr5HCN;%uq|W0257BgUbaV+G$~nq`=zmh&$GT*Cex^O)wY$ zM#mcfos$kF@3h0W8oq{NB8>|T1N|wmp0Ub!4=4?<2G3LQ#0*sgeVlOoiX5MX1+@hj zdP`fNkAb7`&(N<5aqK-cN6yv>9o0%c1#2|9ba%OU9cmZ~Srt~J>&HxIes*SlZf0?Q zX`V^}_5I+!m7rplm})gmy@6}SVWc2H3jH4)-`nXhlJulI47+1q70|7XAr;3x_D5qh zU}g>l&GWkLirYlP1Q={JZ0ejgBZso3#;a8RR>lBk#6@8?^;A26$*8TgU@L%(JR|Fx zp|Y6=@VVE!`DB!+Uz)7ucNeH*4el#}r@e%cfXor&+MM*1&UtSrgj4J58>^C6?XK7};lR#bxEgNy?ftQe&AB9j4iv@9AtN2WqT-VL?U}nVL_x^^%1hP?eR=*7=pn z`@Gv*-DB-EX&3p$JFSgTP!oKNFdCHdLngU~t>-^XcU~kd7#KH^TvY>^z@{x=Gv@6* z#``hevHGkuEkVjB{Zc1v6m_WuBvfNc9*mSD9OdW8VE?v52W7wJjO*a5KMjPb$Gr`f z9``n=dd%B-fr<}diAeFA-Zs(}mJ?L6z&wKlg`rKgniJ-xj~SEAac;%Ce7D(v<&&|Gc~qJ|F;V~i{KMoQ+WQBSe_NUS3pL39 zHudWt{-ce?bd+@RP1vjVBk{$TYIFAL{X#LRl%ST45lv-13B*4QlTa!~Cgy%^6+&)G zJI%<@vOn+jnyg-*+77qH6Tgk1E)+x#yYl>4^Q~p|1+-A1$RA)tF8A$u>`x49W@YLM zx$5mCC>S}`uG@?dF}T8^l1wP|5@)pCJ>b1h6|3Kovmu*n%4lKXM;>mJ+Bi0?An(eh z{r>qBGDC!$lecUu%EqE_E6eupf=uG`%kf{tpLp#lndD0}7NzzAi+2Y%Bf%&xKe5D{ zRqrvBub~j4fWH6>X*o?Y;II)-M=Dd~3Q1lJ+r83c_~?Kw+oQNebsZ}bI*g>v%lD5| zHG0{HNPSuxz>&yuo*!IS)Q@d=N|%#BI&%7+@iERumf671;e%Svv)Y4Cue$AsP;sKJ zrrlM)gWt#vsBTh!7Kb$ zmZoQB=`Pk;=z51qRdO(sPy|$=nh-=SK$gaFH09sMTJxE=$B=a{`N(pZUc@k zSnoxy#Ct4x5>cw_@-eV^LswPJ<+`4SM=0uT#1eNr&}~C za@m?!d7VCM{J~-7`*qKvEz2^EQQ2+T=S<&@$t2vUA*vkdYViz45VX#Y<#hOjoAh_2 zA_Bf-c-@?fZ&}%hyzY4lkDb6Lafe>JC1kHOp@l10R67(IhqSpucA~J)&OPs*cOTvU z1vBCk>*|1f)0!adY7UA@D7~|9l!k(J!R>m)&fIl%-BS_1BB;pPk2oQ|jpwcOh97`k zBNKZ2uphBM<$!TjdA;t=98=r>^oS+zjqT6rjZI6hGa#xnT|dx}S^oTvI}HizU# zM1diGu^OW>Yq|gmWf%3RspF%n2$5^F%6u43d75HMT3?;Pa#wO0Q|yg1i)Q&zZvbEM zceZhW@){6+*W8%W{Vvc7&+y;>rRZ|mLVQR!k}x&$pc&PB`ne)tNq3l=BOH{@j=PW> zI3eXBk1P+|D4f{7`Vo>qDW12zaGzCAwdJU4hbSg0*AmV?01it=9Fvtt@H@vuUy@K| zWl`N^n!>=pmU)9O$(R5Wbb`GIn#5BHZ`8;xhQ!(vYOmiIU8D%1OSm>j$7!J$AY#6v z-rPiqzKZhLW7R=qC2SGv@tM>b-3egSqUq)VwOWeEts*Jg+U>XJ$|;%%f7!{?}e3RYmm{eF&*h zyv#(EjT(vRSNi?kff=un1657IUe**)(!Jd2gdtmQnNMO)3I8c*NekB-@!H?yf#RMs z-Ju9v{rycfz{r+@hCEP3?lFpKD=Y~$8HeSbi%ZD{htxM-ss2<=*D>lAJ<4$utj%u( zxONtx6k^p0fP2GKQ3_`p>6GwVVwl*rS~4Uy5Us$CY==74tVR{to}eW-e$ul3EF{J7 z>X=2Dhx&0#uV+*z?WedxY8Zr>bLJQ-uFb1&KUk?j9IhYm8iX=d7^W2IiXUqq_pQZ9 z>Hi}jt1FJio_&=^@!B$aY5~g;1KAp#@7SI!BKx$ON4mm0P)$)Kz^6eRRDwD zeB?lH#hd5e4M!0S=G^5SqX&$zlXSw88V)dUjzO{i(4CuOk5{c|1Q=+ztP|dlzVoLT zbLl12-36jp&Ouks~7~fS?%C9W8w%@f?=;sIw`4KH^#}AsD(8DFO zCl6x{neF*bcd)8%yM`s3PBYVHkRI5jhQX*%Bj1=H%$oJxdq|ofX#_ge&{lj~K8qT# zsc0=zh~RFr1T^i*V_Rrv{_HvZ*J~V07%@dDgwn=6tFw@30DH#6 zDX$SYfhAD4q+!v52|!qi!HPEdrr_gq+%Um|Yr7|U&V+W~{4qF5yGAg0wHCHD1;3j| z2{b$aH*48%A74h7AAmx%VA#Q}x$cx-XdR>T{3zXHxx`EAJ{{m2peew`{}771Krzmo zhS9e1t7R}n#!?PV6W(I3b3j>H)<%bwAl3gh#ldL3*?wYh+mN8p9aj{FjV&98VGpc& z)!vlzBQV#mmeEN)B_QMAHx!9Tae&Y+> zg*haa^V9StoSE&;Ep=vQyw~my?F8gmT!Qg;3jsuDF1Oy(OLccFRB5y%ikr)3yfkr` zygHtIP|ac;heio9SlIhVn8UT`DraWsEAf7ey^AcpY+0+YWmrc01e)5SX^}j2@#;a5 z7xp)TbHQ&+p}9D%K#r{THi8!sCr|)Lj%Zq)%EZbSn_EO|jzy9ij;v_$o{Bf+ILo)0 z5<)^TZ^^4bAoPry=yl3KWSu**yaEf7&>grRQ@n#4*0(3`Dc}ru}SE$4ILX|RC0RGa!LiXKe~tqf=J z9+JT7q19^`GGQS35WU}lmEty(_KpHUwogWw)4sYnnL?^*XlN7|Xg)%0y{`qVDMU9RK6w(by#6N6|q=k1FJ9eLr(&Wgb${v@_8N@iIuc$ z76DR~jRp%Ckf8(783$z*HaT+H=$RnHXmd&p!%g!%RaD(-Ca|80ONh8E+ z*5wgq!EwIsg{>6JyZe?p1IAR@1+|spvQ5eGr^I(={^PUpUZb}}1#kp!^4T&f7KeL* zqXo>x>6cN306J%H9SmKfB_@g96Ap>2L^)(7COa;`=m}(0t*V>>QY~(dys+Zl>EOM( za8aNZI|2v<4e4*qK_Gx`EFZX-n3E$}IomErCsMj=jW<2NST3W^;n-F!)n(KUsdj}f z&U{K=<=Lx%U^n*{YBw2Ljd4;{4mq9Iz-Bx>=pp96g=Pu>l{X>*7~81U4xz_y9FpAQ ziBI{Qu%+M+x{}5kW#!CN%7VPxz$g4sEmZgFNu;nAIL^$^EG#W7E_N13+$>CYm*%F) z;aI0=E{jgkZx6NUau3`7_tykR`hzxOfljozorAOj5XGFf7K14axSmfrCtEbN_nH?q z+lr=KwtEiLNI_Q1ElOjW@XGTzv3!mB7TmEaZ$ojcHH&a?L{f^U5U@V-(u@J2C_ok* z@^p&1IyUyzm$iOb+urzPTm`3ADs>a4(NkJSo*JriHB;#R2T61PgEYCZMwa$4U+09+ zF12Xbx*A+?N~0_XOk-6kp7b{}2x$mOQ5N?dMVR`_t~XV>IlOfQ)H~&i&MgH16#&MG ztOqvf3O@!p7{goBEsuuUwL{w}&7lWM8_l#pE~$Lz2Kzq5#^i$}>0qA1?v64^c1S*6 zMq3<^%;Lmv3MkEwB+t|AaIgLzsF-cI+zb?WTlYkG6Me0ozGl=Yf~#R&2nZ|D(~5x| z&@6J_KQZ(^A@apIoGJt*UW0oS|nbGRF|zBRc41_ur@IN1>nBEE}?b zt^pcY4XQSj_7q>Ako5ck{>!^XB(+~Pe6q%GWv1{Ux)(;ZXik8d$RqJ=b*KnE9v!O0 z9*+)Hf{#43y|Nyqv@SwCwkBo*o{RNFTZTVA!He8A`6d`iTeU2|iBDdNPv26WVqUKe zR0ZcU^56+RqL9pn!~4X;t)=7QQ?zEkoXi}?@BZn3P5kfYFMj`r$@bLpAHRS3!=L{7 z!>|9o|M&m+&;PS-EX8YLvDNX9Yoga)V7h4SLv(@&MY&ucZGSANTeIFW<<0|SMRHOg zuM`o*Av2(5d5#5de&%rOF-K|I&x>@Xp`(mx14y3PbyBosymWNiivo-ZHoYnti~{X| zM>b2XR*Vp>_jdKCq^vWXd_;0%pgg8xiwy(2guF&nfow9ir7#%i_v1U*B0M>@9elYl z%PK~=yXF`T%T|A+niT&G&@sO!%YH)IO|6GCRS0bCZ#Vy}{z@7utkZ1hDU108)x@r! zo+g$UJIajz)vO2(tmr|_ z_3(hbYvpxa6Mf>Y%#{e_#+Z9Y6ua}6)fTdRVn>X|F(6yq{2OI{*3Uk(fv&J-zHR78 z6cB|&Slav&J-}M3O?V?YOa-5^@d$_vAEhA*K*{x1TX7VSDW0dc51-KK2M@5kl+j4f z@@!kvi_?osi%YYenYr1y#ku*}g*ozh-nx6KvoyOjyU>|lSm@Hyy9+vF_aX1%orV?E z)nL6e+j{JD?<{>qG^h+zCo*Qq(_mw*Vp%iE7;l@AhJ-jY(oMDQUA4y2Tj~Cwf>1+M zl_hk%AQSj|ldl@2Vv2wPOj6;jkvD-zG`9sZ?kP{~$e3O7tPICD@$28vp!de71O%sC ztVJgk7iLS4)vehG2}{FvuOTh>kd)Y3pRCOE(pUPchcZFl2IKa(^O-J-YLr2P_IvX%1L{m-hdzi>ruojz)gg}M0Sz%>eodPyU|=$EOE6Hg z?9DkGgKBkQ93dW!PfOcF!pNn|#vN)g>E0bX)&I#3;c+X@A&yv$YpkOc@|8FG4m2dqVt#f=_>PtG5>i zC;8gR=GSttP!49}FmdeMWAo|)DZ^t9?yCeHpC3~M&Wr?4hQxOi<^_0~bfEWmgyL4! z0&SZWAFW}rlh{-&0h7)lE-Jq~Z^w}?2E&i6XZf*h0F*k!K zOM(KATXgUE1nWi!cO+3^flm-7+Sb9l(l9k;tc2RMpbQ{s%c(ktW%w_iZIwp|_n+wB zs$UZlgGX|i(OcO6JsC)YdAJg8G|+QU~ISY zHt6=xr1A7zEw5;qAEjkE6z)4~g$RrCwL2+Y*m|kqa0bcUx zC?*)dOUyfH$&sd4aN86t+OSFjmh21WjZaudbjHHz!ID>pJW2)*g~{1fyC)3j6>GA4 zIW;qQvk0B1aB7*_0Gcf~p!b*3y#7YFvuuFccFeT@IE(4Vo+jigWiv_#5@>nSm~1E zF`s0&JaMUf+Ih~Y0}5EmGlygd?!!pXDema$gEvRBV-6xriyU)WzgjNh(ZEA!!Kpj* zQMC!e-0oYFez*^Vy)VUpc>**7tg>7zb*j=quvM4klgZhqF;{`0%p%N>wzfTgn6#sV zzNswYLf^jnm$lVBvPCqk*;wCL+ii9XZZGBk*``C+ACHF9kXy9trKg>3)yI}a!rgdi za#au0GaV{lYcy9HMomSn&9jp`NwU2QAk*Kdx|}#dCq*6@+;HLcoO*6Z=c=+T=WPY~ zt@uE6=S2CW(VPZ-YX>Lx01?Zk*KubN1x ze-4n=;A(d>Dpg^9Jbp9QOK_;HWWh6hpkpe2u_Mc?C0nfMAmz@w*%Ez1t$6}iWW z{vS0ORs#X4Cg91uruC4r?y-4c=Bk5#BrAX#UKe2x%p3RifWBI~s;4CU>SUAQd z@|V+olm#;GXqTfu?|HM}PiHr`(&IFn2Jh^?T{kg43tx|`b~N$2Wu``)>N(26T;=UdnR1!n{B1{coQcV(|BC#n;4o@jB9WulW zEKY#)v}T@?S>YiZfKMiU1DmUxsdCLDHGMvb6iPLOg=jgnL#S^d=zj;xXTz(3$fTdG zEiYzhq@!7VECqh|H$eFi)xa_>_9=`^e00nf2IZ;**%L%Eem=j`$mD@2*G!#)p@gSc zR2U@@E>_t~T;^R)igPc&I}TlAs#Uqyu91ax#M4g}{VUEIM1VuvuA0rS8D0%WAv lJl3bUedYCu$ZUgn<-&2C9VgmrIUsXxeyeU%C?!F ztsyQ9WMa074ZbzybMe!lj-Q{{i^_ru9u>?F1eVSA^;KbpV@)FPkhePyup%cqT%MZ3 z3)sF*?d#*g85h)MzE819K*2H++bBBJW@)9)sQPkmyEF3T)nUU)t|=b(=@P{Ye8hGb zqooZ=Rl6bw>Y|j8+NIg-sO{-e9rpWHp_vC!ri!c%+og?uT3h8p_DvBfRgnNMB2zJP z*z_gdrJSF*J@t~*oV`&b8AywV!?Vd3lYCOf|mf!YOOw5%rnAT}c=()GqV=QvN+2G2v+*?2v+JZg*)JB5t z&6rwk$N5yPXY0czc;fT&ui(wz+MA!DzXm75cLtLw+ClUzz$^#nh*7qLsbw5t zVTMP-_Q_gKM@4imJaVEc(|6%WCd?}Stl5EXv83#b83$&5#7II>j_tc8dD5d)mJ1{sN~P2w8oa4=gvVv*7i!o zUp);ks3yNl2z}zV8o?alOqP{|V2baFI&uwjDJSuU&JDjELI8cQ2^}JE(7d1=x6%TP zqh^;|Z1~AA+x~aN4h9_Sdx0jFvuOOWCUSauW`1#gafaMCJ9N#nwFDhH$$&5Vrd)IL zi?cHnjV#W07Wx0_Sv9j-Tw0p-y(bb6`ML-6dn-loPU$3Twvpe=Y^G{1oeJOtyX(oV%&AkX0F9 zY|CEdfiV}%<{H(=;d=~@mOkI9Ok%hi?IU<#y8%ZZtshsmU(@aLr?rvRS;)h>}q=@%pv#DaS)0bN^x(bQ7 zvpfP?3zlXSe|qhOTIcllu$E0|ixX8Y9?&o7Nqjl_>DxoJaWMK`ee|fA7I0Q#+C;bh z>Du`UP7U~55EG!A!e8oX^UACFW~(NZ&q|zKPI^tJL&;0Es$fMDw@`$e+oM7FsQor- zf7fmJG?fLl){|o#-|cGs?G=Cq02ZFlhblugS)rC&gC%U?lx9A5qk z3N#qFF3A8zILsb0>1G_Q2FP|004DD3y`?@#l%iBO;ISevxTUmvJh>Q~Pz+y~iWk~T zJkK~VSO_R;@C*hz471FgG;F#0ZHR=Q)#lO-fq_G%=ndW}yp1%ufzK!v=Qy8Gw!`?4 zdbUIsrtdhAr^$m*%a(wmQmiob;X6U3Y8>otR^Mb;OlG&>?DUK7BaB&Pjo<*~i ze{lpp337^mx}7S2sF|g#tfv?Q1%IUR^CcoFE|g}&5zA`G?f7WozSh$1L_5dFhB*UB z3e{TEcj4pR_1hcjW1l!iXmlK>{EZuXYYy>n(agzm86&^GgQlqleQ&&bYfj&0Ea_5=49<2yBSh{~f zajVybf1zf`JY9ZYXk|(|-ie|?zr+lYQuQ#E06BW+=K82$xvv|uk}$)g*H__hzrK6w zgfjpUa~#(}hEase*(Gn49H%y(6Ndp2B^6A24datA+%gks1!_T7g&!T?o6)ybVB8*@ zChwru^;O`0Xgm$=mb~C~OUe*GR6h^^^l1ccSS#sZsbIa~O#?zxNB3n6J?@0+8Y0v3 zC6p^X;n<#vO_jU~OjqAf&(PPd)hb$|A@``8;KP*5SlgRa#QS4$JB2Fbj(S6ptko>p z#Xbwp1TcMd)qen-M%x|R>Kp{15?_`{riuI;R#kAd0VB0qKrE3a;9}6PXY4*t-rwI( zib$EHMOTPR@@WVE<5yXm62C#%H~{nlNn$k=#wO&uK6ub>i;l;y>&5n7bbl6G9t4-1 zztk8OOODh$piGGXpIUID3%sH4Jp5gL6ko>u*BTM#u1|Gq`iR&Sulohi4kn*?5c#_L ze_13X4(lWl+5b2JailgzCaEHtVi z{VI;~PiIkHxeIH$Nqo*)Nw8_TN8$Y&?Fw*q#UzORF*KkFAImprS&0T2{f2UaF+F2W zm?m22ETa0QyAhq*Ix`E?3$xT{EY8jN9+3;vow-?r2{bukYx~ah;@slG^mKQ&i%wxd zts0YTP6`@&tr<~B{Hv0ytD!{nE(5mpl`h}rd4zw22keM(%4zgI(F(wPmqu-F*zjgp zL9e){rXKt7$?PkU?9GExD@b`*G@1fNP0`?su8JY)bxY!!VUfecK{nLo1j3GSu(PEz zqiYC{S`JxA!*^q|>nh){ZlvWGe!}ziPo5Cc;x)Aps{G4OoG(0|T&ozRN1JB3Q`@!F zTD!R?z3)?B@@E!Uvs?%RtqPFvQ4mG+3W-(==&U4&;dSOQjkm`yyH5u=$Wo41_UE6401&|lbw)G!`MrnHnPegQ$aTUG-8wjG<#w=ZvfA0pWWP# z1|h8f7>ZGyYmDMNvWC#HU>m1>5ZV#&NRx4`&)dS61->|I`cw zY87N$wyFR81c@hZDuDl3Kam4rQce1RTC8UKIYgHx*Vi(cp9Jd$=Et5;=EI6L@l+gV z;Tu~(%+Fy0UVwGDUgerSYevAU#!T2kEoodOwijq&uJ)7^vf4$$EMP6cFVxwb(`(yw zd$#sn#Og^%E1-D2w)-N|Q9L70%31_LB+acFo*622`-(|qk?RBSUvUyuM=V2Vo)=O4;$^*u+`X=Oa;)m0Qx z3J+Ckb3SJVuOVk>U^?UP1eGsa|8@r`{mhRYNoXv9lZ{C&@GYx?2Xt`|-5}gl1Vs~w z&gcqR%b3F6)5(^j1Jjx|vn?B~_K6Ug)~CqNFw4zQ2k0f&P<4mmv3^0TI!3SRobbL} zL$l-dc(T~P->+lKwqT>~%1vL=Lbvx6<<^(?SjEEAJ)SM^FL?Pi{AslByIth>cTlXL z3?_5z#w}bRATrXdFae;P)%p2~3Ymvvp_ixVn>%e|+Hcp@VSu5+T9tATSA#=MWFkcN5MQZ?>q zvt`j)GV-0oLgYj_{E%6-AeftK#`@g9C=sqbOi&gG@kRVordbrsM0end7m}c zHn@OrS6iUYT&J_tUE;0z?lkSDee0+~cs>bWY?c>%lB%2H zi18N3Sio{ZYBK^}c=&9=x{O7L;7`>vU_x=;HG)zyG7q+!Hn#1Pnmt3-IWZHOXX_sH zMe6hd^GVaFfH_;X=@buLwNY^)O`NleJ%4>>JCO=e%5QoWpnQkp7!ZPvhmkfIIdVqh zNBnN%jP^S0xOTlLvobl=(g~?&S_GX_DId+Kx4yB|0>AyhcJx%wFd&NOc0Qi$xM~Oc zlAz9*y)WG@$;eNN&ehy%()v5;hH0u)eTOAh)X(GS)3`GNl{um8_8{?VxjVj0QgE`c zWl@U1ry+?uLr8oHZaw~n(|0aQpiLzKWRH39Jcb%p026%|GDA#Cmi}rM*#<7*6{9hL z!J@S(G}+L8I=ryD~f6H<4>>G z$(GW>O)1{$Uf3-Bj0cAvLh%VEC_GR_(*FAE4;DUgtRV~t8q!p$shbV9HaY}V8+Rs2 zTg$c&+fcPq?_ghr)oZsQh@+P~S6Amvvb4qqA0%DcL~*rWV7>EXX|2H`_M#!ex^t4n z#(enDNJqdy|Bf$pZ^-K?7z1ot`5$QnWGAI{^gHWg**@lUne$&ah22mI6R zc(ET&&5~Vo;^JfH2Y-8Dbwn3)qC?+_D@C|^z-BK(jhlVGCqBIneXY)Tr8KJ|#pCa4 z|9e&evY1HH@ho@mL^|1qV*@P(b?N(1iLlwz(2P5*~2UzEH)JUL>Ivp{-x0%)(L#PQvt) zpvU0)i7q}lg~Y+c&J;&9d(? zR5uTI>$g`2)+cDDn~2vgm(0cyT{xEtvI#wuvzqC(RBCr?f&Q0~i^#ikh2_;Gb|`4d z0kJRaZ?1~OVcXI7SmMFFc`dS&J5ReFECnTg6Ce&oF>8fA(J(Q=5B(N9YUhW3B#lcM zImE3r2JD+A(bWSE77E8Z&>6WFTf&t!U6_pugJw{jE5KSBDr&Tpl<|#P){L71^VT<$ zli#o!>mFUxP3Th1)6=)pkp>S?#hcd?~ z&3pnIR1^bt`%v56^2t-G=7&$7QZ+$*>J%o2Itg3-Fm~6FluUv3EO4m+^Ob+($AepH zJZA&9p3SKh+YsKVvdb`qV-GhT{?v1>Rb{}Y^ZZiIG&q_*4O?K0QG=5$p$pU{st&p^ zeDnAJ{H6s~`R4l{zNze&E9dSm@$a%C;fwVQt8?7Or4zS|87^qtIV}4#RF{;sS42+r zJ=Qg>w0Lw}VNW$S&9r4Kn_(3iMsQh>WwrOeIVymi+9YnaL5nDr^_FalpHs`bOK2hGW8CxWBe(b3bHCGY=qPycscruQ8&s|=>km;zWEmL+` zifLPFS?FF(hU$P-dVYIGQeB=yhYX3-e7|E?jjwp=S9j+hU%`yg!kRdhxvblwp1KOo$V|x zbvsLo@JI7qiaDm|!D-$3`Gqbmr)|HoIvOLuk1?#HG4>3Z?#^hVv&E%@!y|Z|lhd=m zemuXpyt@94I?%gM_n#mBmY0!jrJ1m(+#=%@$u4Qv+eMF^jcxLiZkiL3y}F}W97Tj9 zIylS|`58xT%dLNbxl0r7A1?{K$arZB5#`kUa`cKqAF9oKeK`%&Ok3HRmR~Y!)<{eV zCawUYGmEKE2^ilv$CFp(jI1^E=Wb>PUs3jj5z8S8o!vNY9xD3bvK0lZCa#i2NHWF1 z8?W&eN~;Q9ZOFkL8 z?3{TuU#U0~Y$APh^H5=-dK*T0e5qtvrC8QNRgt26iksUyyIheqrJtXY<^j3Ph;n1z z=+*qi)D&ux_Wg|Q#=)*=Q`1XJi&FwF*dO*6Q{!vhhuU6$KQ%F=sefQSGX;9z&Jl%q zDs-rY@8;EJJ@1Rq3~JiSJh)jrXpRO>CBl+?C#&MnWE^-vg4Cqidb>Xh zBCx+R^++<4qslcuB+b?8&>Q&-FR|u1C%0I`cn5BIi;qFs&ocfsT-W4(5$pf4`xVH0X&uiI}5l4;%W6;zLV7|G<2-$7^Ij$ z?uAXMh&4v&kBh-miC;2!r}br#X?+L`Gc>}6ti7p$yiwJYaMRRi_d^^A$rC+Aw{qFp zMetH!7-KxRt+;D(1yJBv1~q26BU8%5bbwbbJknoi#`pqcg(5`tMaueG8G-|t{rJGX z25a9cV1wB(Q@L060%_p1i|ffBRBurAe5pO@9-SpO_SPyj0b^K|V-RABj2NE7pwaW% zupZO!cinEcGdnxqnO%ZanxQ!qm8GJKW@gw|b&;*kK`(Q%6qORkEuNBy{%8nQwR_CX z!F>zf?9kzr9h&f*RB^5omUpv4H7Eevlk8FNk?zc|(KZFa~&k`E5<#&ExDQK)t(p#Gc4>2M2qmNDsRP#zL zD)O6brCJIoDJxs4?Z+os+WiYBF>{!GqPM;Pw9K`RL0YUt9v3*uu$C^)pq9TXypPS+ z$RhgV)LF5D1*j!zYv8A_V&@N4a}_yvX3g+S!tFkY6i2?hKKYTnmLZ@zRI2Algn^z< z?7gs)s>R1;1N`gNl*EnU3zu6xtc~s_9f7&SbaVzfEIq(o*u4c|6$ApGt6Gtja@_BK zH)*4Yl{U#2uP0*&EHPW0s!EtlV+$CR`#5CxsiWKWk4SqOQ#kXOHss-;EmsvB6?5G? zeCauuef!!;hSBOL)yK3k1WB*~r+mwx=G> z$u{h1mBomkd$Ll&OrF+$NyR z>CM~CmF+kCv>w}g`|92L{_fhFjo#j_rF8c3@9Uczt9yFdQ|!P#xS1Kndw7)KEtz4; zKi&d}fp*%@4)1ly1rvrSG_!)LiiX0!vC0u5jUlT-xl|>F_(rloghL7;^dab+hI)w( zG@)}c+%vZP^btC$o#mWSDwurO6+<+8;Z+917wP5*2j-pO@zSBP&p8?tnN4wr!zjpo z&rwBD2mKAwvKFP(*O=y2L&{)!iPi@?fB4A}8cz_cebfxBZR4z0+My}Z+cs%Nm#2hN zR725Yo9m?Ni4qOoxl>^mAD3YbAK>IcTTvg|We~KT{0WFv7lw=HjNpe5t*FEG>+Z*8 zubXU`9Wy5~x*Nx#cC7SmjP$Y4gNJq0Xocn(%Zj@)2~MF>3RJQr8@FWV1N4m3dPG!> zkWgMT*}#I+=VqsU>oA%OvxB2_ z$%xbOA`q4t=?nQP_C4zlO}#_V3&?EYJNPSV5)Vy99uC+6aT?k(pZN;AQ||mVOD>3+ zOsDPg493tUBWFE9n8aDjt!U|ny;B%ELi6N!%h<6^j67e%-Rb@B@~J3lBkg0=E@O1i z%p6B7mLg#qs@*wLqX{w$5#)mHjp}z<3d14Dyg)|2^Klj^$(+%ZmcXG(+?f$q=i#7g zN;0l)08H;(Cv?W1#X9`tMUeRzpS=3G$EKIA|Dt`@NI|3(K_;oL25~qVmZ#yKHB0&J zS;-{JF^A7dwtq9ECxo z$~`_t2#zSmLIl3+F||^C7h9su2E61~T67{zx}u&KqjrUGUy>lai%}1{TB{n0)n>Dh zrzE6h?zyPMFeHNVBv152==P>G3yMnwzvfFua)t3+^Z8rau*Lv2>6+>+woCi^SxO#$ z{Ve4Vzi`&*ly^{$O2fPGO#0S67N)RImB~b&5yB z%|mrKlr)!bY@uhzsT<%KFsk-Xp2Y(M)I3Eev4%{T~rdeL^VdZqZ8VdKVcN zB_eouBkekzk2 z9yN%Mp@%gL$Z3diwuC^YTGVh12q^oYYW@h11MnrA z8RP){RmW#+p!BVgQ)#$KOk;Fi^39Uq6o$?zjLvj7nfxdmv)?w$nbxcr3hYJrMjipv z$u~{F>`GpVT-|`2smg(&xw8ehqn68FT^aqogl)%4og39I^;5A&7EkbV2q{H4f)*{! z;9#c%jWfEXE4BOQ354rb#_{@xGr0}U^K{B~z6$wjVOjJ&+P>3lX9Bc}tWjj~NYZK&3A6UB*MMJ&gKW1<5UBDh zOc76?w8v~>uf5Q2ueg+{gX^=$r*OfO@jqYf`O$TFp}Wz>Y^16Mrh0YkU**ado1ANPGlA6vw< z6$@eEVMem`qzcz}K7bdNfI4=jv$ux633N2y(#2v-B9WnBO1|kpLIxe;L3#O+`%8pI zNg>XI%;vH2p#wSe7HzZ_Pd8wRxD(Tl5o?9$_RN2-#JEqKX`lSQa( zPHvZ^?3tFVj3YurYGGis;8$|5P(3@{s^dq!Z>2-9BB%?K#I{RUMG-47=8Imnt*41< zk;=FR*SrJhQrf|RukQ9bw!c-=?!ilCw9hUf0)6$ zq;pD^h*=l8oh%3&w?UI!ecB7H_(tuuM=y+gK9v*ba<N~?eSd;+(Jb@r&kED|A`vdB>F#(fko{p}Cm>gUX%$xAZ9Y*u9g zY0C02URsq?RpbD}5cd%$G=tPea@L2&aaVOW#hn7q@Hk8%zY0IEkxv30~J_PS>- zR-#MCn6T~WRV8|41_)49=xZcT5?9S+j&1I3T7Z^D&I@X`8?6-e*OwOQHH50CJ~loESj2v9Tkm|&?O;w)cdNa#G7Ij zFkMoKfPTAeV$|vDxBlt zrPBLj6E4oPTaQq)RF)@VIvvqgNQ_sJ3s>J*x)}K;WCdF(i7>pi>LGGPAUEk)nN-ZC ztQNFwb#hy`M;HdsQ4}J%0XmEGQ=FJ%^zQoPhLUs4gta>e#G_Tr!VEo$wnKRHcIExb zzq)}jzrmI8F>Oy)vl(XXG0s=R_8Li$=babHJsA7_H5?;9u7@^Sr$hq96}d(W)LNL2 zM<}FrDbt3*IAH=p+A j;t<;;75Scv>eg``>vOc`2Ch7Z?<74_Y#{XTaaIH&uDgLR7tE>DV0n`vs3R# z8vk5>2wz)K_lqd>|E@+K+pY~$+N46;7K&o;4TDjm*lWWpzrO~)R+uktm1E`@R<-yr(USc77vYjT!!SFk zS#+{@RrwBk10%qi_7j!P0)&0(#IGh!8oH87l`D2$v-g2u;!s;Hs)U8fQ4unBXW@?# zxMtDyL_cy#j)m|^4P}KxA|8p6%`SLV+L=CV)r~ScZ>f~4s?{PMEpzs z=^idjkQkWj*TQdIwd@g+w}%XB{6ik_Y~g0fR{%s5rafji2+a+@RwT%^OtZ85V{S&h^Y;!;RxbOShKiDemc32K zEz*A^fgC>;Ld z`d_TL_cAaZ;>j)j+z+_e+wjZ984VXCX>&g*)qLS4n!WQzwpodOd`^nVXKPcx`va`n zoP06(Ih&lA4qbEx$Z@3eiJ!SDN$=&Y5Ae&0FuiI?SfFUXQhQkZTPp)Lsv5bMwzcL? zy5Za&^Z$lP6qJ+fKBIB)_!I#CQ8J)GzmWwPq4z7#ZTO|;y1(M%&o050UXrXZ(^sky z1&lvJHBSPtHj{O->s9TsWVz8J*T`A%t_L;N#mJFIF4U2oX4yzJPEZ;>Z5~Y%K}+Bb z+a|m4z3#M>?dEJ(lH9{lWi2xARx%utfqd_onR;;~JnIoBsx*^SelZn)ov9l>Xh~tlobs_R>d|L9#K$rOoKX)7ZVB&(<1Fx{k?@Gttw^6L>P3eG6$>&-;$YU0A1E(1owX_PTO!(9PUWqcH{%@ERn=$JZoOZljXl`Le*1ZLO@vn(MJa z0!H*_(|SG`S{f#ZPrOt(F+Pos3H3sT!^(*5Fu{#4_-f!-5s!tO7T^4xq9mB(J1rEh zUGZAw64Va>`%8(C@8F`aOuBYS{w_V7Z5V1 zasj2Qe6TZ&s*~VEDGaw*Wu&*Eu@2b=h9FQY`!xo&l8j?tT0Xg#sfPJC<8Q%-;W%ry$f*|0Ns=}?Dj zH#fRNX<76P%a62_N1y6u!cXyRbz6y%oR$xa;9yL=`^0ZgyBv2;HJ>v+uzXB?gZG{~ zHQIR9%M?Z>dbI&QIl?A>d=I}3pGjnm(_?TZMXnf`AKVO-2d{091#`|qLzINwTaMv7 zYo^4M0Ei=@fN`j?|MmrA_g=^>pz>?XnJAjLyHsJi(Q66?*>>8R5{M$VQgLI#eO4A{ z8m&akS7un~O!CP!ggTQlDStlVLZy;?PojVdNmVe`_RJlZjh7_ATf(-;$mazpsCBnW zNvr>H(t7R~UF-F3SWdN#rE$NtML^p@^Y$fs%I@Y6u1DgtcANOD@KU=O zAR5~6bc$N+&+j%?Yt)@J?Blmeo>Bga+?@DA8T2FRO4yPa>5*~d`K`*WGwa4sgq9W0iBqBTNy*Q*gtpYH?otzsXJmkxGi++}g1u&JRRBa3gCCeO zUX@jf7o5Jr_1cE#Dh6QJHG(C40i;)Km=e)6&@E zlh@UzOZ8~9d~eNiVAaV$ z$L=ls%F#zi#oCtRh`TK(!b4RA!^*LjtZI?Sa!=FU#s44v$)cD8MopFUST0Xh{tqPd z#Im+cL^g)3GQtv-ty8F10R?-d1zucKE#Yhj!%^|zMw8Bj=2g+3U9EYfV~u1)#y2g- zRL{{d;nkr`F8AVZ+7zjECob2Z@3qj-xg=+2^4k5g`(n}~GT}wv?0U*k?oa^CX zM22;QpefH+Y=YQ>7z9I-sUT#vz{VT}6V(#5g~F>jwQq-PzZ=*ssW%Z%N`+d@Rqb5- z9rhj%Sq?IJP7aP9au0_RUL>um5tR7rn`(B&Qt(XNrz&oH zaIfk%8E91c!xn3Bl0~QzT<*lrs-p6zWbc*Ntj>K()?S@EKHkQvc`~wW$f@z27(V+4 zo|ElG3v7}0Re<8zKU6@|-_tmZa;x4I(dEK2q;>h%7-txBK~>`*W2U;PdlgLk$=FMW z$nCuhEk!rH_NwCYUOwP-e#^GyGdRUs4MESh-(DZ^;7z#Y-&|&Loo8hZ4+mSxz^)UF z)&t6siNQ2pRl)-#L8?P3B%8FKsnxY9K**Q;UuKnZuF1|)2 z&x2%bUs;~KL|Vlw22`xJ=?J->AVK@m-0w?C%u9`3{K5C?iY;wQm}Q#=VpGr9LfCy0 zmm?hTWv8SvgK-7x>FvWLI$8fe@igk9u3bGVSMyXbw1V)H{j*3X?!mzYcR2b4bB1{s z$OES3a+n5eCeP*Ao<*6YMf*HFy}K13WpPfsr_V_LV`NV5Ca7j7S$EBfy7WL}^b%^; z5r(STu`NQ4=IpG1e*{K{x?~ECqyl19MQqd%0I~c+bQEDv-gK$3R&zi;jf<#6?mRhb zB^c(&WH;$&gmpC~Ov{oL$ij|pIML6rWHJ!Z>Tw^OzpMSgS`(lBSvK#?RRk>JiT;C{1-70x#Pc+XJAC?Aj)!nI4e=Joi9ZoYh#rIxVcqz+>zNDB{ z6j0b0nWvZ+Jfh3i1Ui4pmp-MSEsb1BIoQ_Ez91?Y?9+x6;}}qZV&d0y_=7L6&Mx87 z7YITJPm#!LfTs`I?A4)1JdBA4({gIhZjqEF)xa#&jmA304O&zy`7|^Pn_O8Z5q~&Y zI+3=?A`#DP6w}Kz{P=a-n*f>(Hi-6B{Hm^0*QXEcQjPbpwk6D`xQ6F)jxkhWTz90b z8r=K|ukg^C?7y^R9hUaRGpw`Pfn|Er7p+%3@bs>=fR9$SRlqAH_D8({r$n+;(2i}b zA~E&cdT5oiT^1X77CwaUbT?#+0VjYgk-o8C(4AN{4XLyJos%P-_@aEbkqVys@%p+{ zLew8{Iop_k|M}VC<-c}zQ@-&py|@1_d2b#V)o~>LkLC&80^94f#$e~#tfLcS&L9puoBe)4fDreM!GO(u-}mi}b0QNkPGSSjZm^{p3CB5hb8NCl z_WP;o_hv={Y$yBs?+5BvuikX`>(^afT~%G(&ADR6&ZT1=OfX9xGFA)aL`II=4Ks8> znFx|aMv-x*aD?hEavT}Hw74~kMYn3SoZu;fxU&nsowprZw$XRoW5m#*zDdRkq_qI8 z%IpCAv7Kt80>EuGn>N~tBF;S5?e!JMm%EKkQ&iFnGv9Zk(C0_0+cxL2CLn|^!7-wm*X=>~U88)l{?CG9z|y)!@|5 zZm6IT#ctThXE35Z=Qv)dHj17H4>#JK$UISD!3$ZBg2P2u0~&&l$YZ=^`!W>;q9ogXt&6e!m8)JC zQS{6l+QY+Cz*5!EVX?yaWn9x_@-fH(Ln6Y2*#qoTH94`-C1KvvSrl(EiloAFB|b~SRU+)l;F@$zlfZpet@ciQ?EBTL)p z&#GQJ^!7fHw(G4_7d-)MmTcx?CSzo-i<$lCKBV}U*)v#(%HkIs8GIvtDyq>L)+<_{ zVY_f?S*sD*lt^69cokdjlxNB5EHZh{iXVz)kd8+YmxT&#tO|TGN~jM7vw@VfWamc zS!df;tv2gn(Me;LxtZ}NH}<4?@hP5!_02Fm`;@^!QUdX}8RX5od9jA0GK(Th%Sds= zDrPv$XW2!8$uCDjjwqbAQlnyFn`tup2QyJ7(<^4EU^R}d2xCfRs5a#n$~K23X@`v3 z{KO+h+`42A5aS?+gCiWFr;4E|ihm~UEFwwfyd06~?V_}u{ei$Z?k;u?h;N}KZ}J)c zJS*NEuW`K>_r(#6e!}9oOyL+q8R2!!Im08rG6ADz9I*{XC-84)niBGnan&h|<4AtH z_%dJ1c*4#Awgo^m2WBpA<;wm^j_RUd*z@_Aby+E(OPKqz%0fmyVOb)OaGJ*?;mNsq zPfeLz@KjB~gW{}MoAo%05%Fj4lT+0vU(6O_U$8mdlN0pEPh#D-n(VjvpH5|c3jfo& z0A*1!K$}Ib&Jbv88Btc}SZSi0*w_n%nZtoQLSyZuTc+W~<`Er~Zx+RS=IF)@A~w?n zN3*$C&NNdVS&{!VpKc%R1vj&oDWhqd1Gr#@Leys0uhluLQI%zKggb;w)WjVt3&8l!CEa=bd!90xdg}x_aMeKOOEDH^9KNV9DA0Y9v z3}gGU?&hm5$~sX*X?4JLG)3wHGcDZA=4^5zDXX-^_VbMGcK2vx<6|Mw2xB$*-_BNN z#%QX*v9DB_`0e?)-I?tsxmiMQA76akrHo?4m%|No*{7y9`Rmyam=yEv-M?j`)7d-< zLO^72T216C<&3%j%Qog2?Q z@v8K+pGcxl=jU0u&J>I7320<{As~)fM<8)l-WylC+PtW7E@CMxd`NaM*M09I0#GjW znlD6~_emo_R0xk50-P$c4POxsiwm)0402+oysEsC16d$waWKeoHc6ldneCB` zGdvD@dNP7Ka0pj0dZ+P4Kr$2i zI>8_-(eTS1i&>BHZ<0#zI;z>sMQqZ~lut9Gp-O*|Rc{APyifxew-DZ5%7Bh$_EwoXGi zJY7$toH%B5QDu1n+15@j5+4p}qN<{a)#Idr*m1a^0!eFZbs=#kE1FCTTxLIaZFV=eA26!G#ORtNWt35 zqbki(WQL#TA08&Xfo|^DoUj~BL9oDRCpFqH$w z#H|+VIG#jN`%asF9?|e+*NDieZ6lb9kx@!WJW^?p0>)gk9!z8`g?x=1YY!QTHhiQq@g`xYF`hoG zNf=^Jl{+7#3dd?wp_#6J(}q!z-og$Rw(?t^HDdTkPJexP1e^tR%|7jJ zID4l}E|lrfs1#!VWcC&^PW~$FO`IS`31kBJTg(OpG=nZ5QFfy|0<)LxcA!fYsVn6P zHcKELeIhzzXu3Mm(QWBXLc?G@j zRw7g6AZvY#$p~Lb<1;rId0rZw#Lg1S+Z#4*J7}z2FG3x!SN%`A0zEHg-p>eFGNl+9 zrgVwG^Ga`6xz7$^(sJi{e)F91(Rz z$cV;a%pCQZ^+AJKYmR+V{|qu@u+mZAhBjXRwzXNam+!T^LZX?tiV=mT1a z-&rBMg!j40Pex<`T@hq_VWeebw8vGZzn+Z2dp>!ZE7JdEs%!W&cK5%Pa3JidJnLy` zOQ-H4i=<>4tiAQr-g{OyNzhRZy;1t6qf)Eyik(JWm2nkTw+*_gqh?E|mJ}kWs2HuP zW#m1!LkdR})6FMTKCf4Yp+%lP%BeXi?UYj`lqusUnoUUTX-aW(g3Zbs9i3GgAa?gv z>>eDe1EWHbb&RP*X8>1k_{ov2%=+4^HXP?*)eO`LTzZ1D2FiBFY|}q)(WN5|7_sZT zqNMuOhF&aa(~LhT)&;E^M1^VDY*zZF@FCexqB-lV(Hn~xHXMCZT* zit)}iJ^Zyc`?ftPW?YO;2RO1Jl&_zfGg1>h^4LS4FJOR2EiHJQRS{z$)2TMeIPDhA zZ|uu$R^7`m&15daDDwX&Y}%q7FsHWg38YEZb&F90uN&qIosp0y{j&zj0gA#cW!H%6 zs+)%IsQAr-G<&fTBZZU4Z3b{%;7ccBgfi;Nc%S#*Cmcnmli03 z!dc5c=S%?;BM)%*4QYAgXP3q14zj_F87nle^`S zEBIbVr0CkLT-k)x(fyra_~8lSXP*~)=v#6mP2_kMD#N()8`sPMwlW_xY@}#y*u%Im zu}P6JJ$8N(rIW*Tqdyv_PBAWs9%CCijSWv)Ks}q0ri3Bc=b^OWN~#fvDA8`!6G?C0 zgj1AxXICk+QwVBmWCp<*mk_38orMZw!a-#yD6snlmXk@9?0+g#p&|$Bq&Y=Vyf3_`Z$@7V1+sY7SARMgJ; zknZ00*>suPNh47qglSAimaC4;KANWe6pJg7rbsK-%50;yCDWX_tca#dI;)^{idGtZ zlq|K=MR!md_mgXHyWa@|4$`P>ttZW5Z>R~0;*DL!>BJ~FvhB+j0TxOUCWJ9nw87?Gh^6=*W1Kg znD+^4l-ON!rm}EVwyaWiuJW`fUr{bqMe6%Ow3x*sihaMw2;%i`JR0 zzo2CXWK~ToXFUd;7p$rE?h%&J%+nm^+_0;o2pMDz8!%x|N zQMbrI!XQWIgm<+F-(uH^{+MGvO%aKRYV8L;=Tq&H%iJJhA+sdU2}Rn;*bicSrFeUf zmCX=PyhSZ-vn@lE2xQW!-4de_I5ka{ENq4f6(Ei37C=l1Nz)jL?D)xb@7+2c|hhbzB#w7NnPw}~JlZ+Yq&zMXOYopA}4;0v^ctb~~S7>j!`6O{Y zu^?)|u;)H=)EMm?P3t&0k{2ZE$g{HNoj6_~U1PDcxqt|!B15Vrl9_8Gq9VLR=LBo1 zP91wAUayf+}t@wr7+v`bN^V*|%Tqgito-Bs+b~Jf#{p+e(18 z;3bUO2x+#5ns7CvCfNmxnSP2)+o*ejNNcpvZM_sAHDMdbJ*;9evrLfz?vpGol)&uQ z!;mYJPC8!73Oi1{uqSe`!QDKOqsl6^%??erLxY%{Y|k#~KoY_J$&B3Ee9;As$}1d6 zOA)8ox0JOUC8o{Xtg{3`Iw-OYV)JCt)K!(SEix6w96M897VuPhiYxr(6*U~1=Vuv3 zfMbX&JQbx?r4=<*WfkQdPgCI!7W;kvK%l0kqPU{eQ|+m&s4A)8zNWOs?_)zbPerh} z+~cqGR`~-Zm7K)K0e&PY^Hh~^NFR@V-ij)JX$gDaR|IOXOQ;Vt;9% z+{0ff+r<_a7gq(!s2)2Ov1G;U zbZBf)QN0mshE7V;AuD9+NI-P=Acin;KELG=(zQ7Evj7npDw=5wJ*Gms zXubWjzQh*4ED9qPN+H~$f0kI3ZBmBW6Bh$$EC&R1S0hEOr z7b{rM_(Gl8jxDS+)6k}1W;&X+5AEHh+ZZq$qlL@};G_^1Uad2SR2PhQL#pkHOfg* zRP-OA)2y<_{!_$%C*>`T8hkBfHLNzW1WN27^5tz(iZ_Jq8z~Fgk;aD)d*~5FSB!$P zE!Z@y2jXX`O>MVlWZXd1vCmQ%p`I_y53xadWi_5x44P(Px4jwqGa`dCnE-c*(O^|r zGK*?@8~f;hEHt+b6Fu zMb>!Pf&_Eo#y2`*r#CANoV8ur0VA@(qYp)7Q%Bk~!-*J_MLdb=ICS&T<8^ldyFWyd zBDpYH7^NFCg!)v+dgwuUk-lVlO?yTJl}xIJ6+ubWbQ;}sN_U=6W~H-Njyd2{#oIPg zE&07H`@U;K$M&|*pv1>^#Mq>fqe7h}4`#y`nY(JcO^T5f+a^WAqfW67eF7(gg3U%^ zZ9BQMH|S^T<;YgYfd6fAZH)uK7 zN=E=ykZ*D#H9DROTt*F)y?>x9qY8>;N?3^qk$DkMW6>;3XHip+v7bbb6tQP~+U&0! z)gEnYOC$^1Th+FMKwvpW8%LuouTVygIa^lf>8zAWeWrvlqMwRjCfeASIpJw(h8|`V1pxv@ba3g<hzlIp!pfbFkLp|On0{#)*^IirV`9pK@W)7izCY%jfGRy zU}v*2ZRA*iD8}|G)EQ-f3{yEHIrhTwD(q{|KG{si$f>dJu~BLeSSFUpRj zHgz&k=0_YKS$p+Il=Bz|DOIZ@HX4SDh&+s%jZM@xJ4RIb*gXbs*5q;bm_&iwEA&RG z5`oN-tEmvw85J#ZrBmpBWX?y^Fzjh3g~vx|NS^KR6zdCRGBjVQXRJvLPvp&ZT-!OS zb}W`8F_1adD3P$Rd9W)tG){eqC@Hsw-|<4He9p57$8N7(8a+yQsR(ONjTotipcG9R zAB)?ST5*T3ZQi{0^+_^`8;-To#`_h-c^40c4 zQvDJh;^L0S-Yz=oGC!g_b~}bQW6ByJ`qH4BsZnw0;isM${>0;tKJn;CX7?~BeFL^p zg>lLz$D%4szN5qd^n`9w^Z01a8vDj=yx^b4V)N7_PSSzcVd(I2dqc741oTv~J)3%v zA-jK;Z9p6bM`1T?BONgwL=+6B4ckvE8C}{Le{3@-c4)?T76Y;9S0x)!r?IEVT)K#+ zs-ki}7@M}*pC%s_sD+B%t zPfdxxf~D`3{%T)om9GT*TuFtuw8~rKtM>XTi+#nVHDyF7sScF*%ZrOWHJmT)@t0PW zd4151-(T*jDK5t+wQd_n`7LX0-X^-b7{99 zO_UVXGtL$}A^8|b%Bb%L98a_#E>XLf?g$-Wrmq!D8Ez?f+KjvvdD@NvhK3M*8T8KM_=5m&Gn2QcF3_33%tX#B8L3Vnv3edKSTLccz}_cB8R2a+VAG)gT?PT4_cLV>kvgK5W825hDA8&V zl759^EPn4YMa8sd%sEN7T3T*+sac3Fd&ikIh7+O4NGFML28qZIf4!;4HbW>i$Fyu4 zxjHvzB!>b{K%J0|6`97jmwMQvv29+JXyTn?tAc9B8_xFk^b9p#imYs#VVtc7p%C@r zZrh_AOH4$X|KG?m&+&OMmz z$3-i`q==PR#OCmjeKO62No;2-#uR5URK)5p`$$HuqA`SED;qb}Lu|XmaA&9fVyEZm zaI!^$Z8w@~Y)j5S@A!Zl%Yx$w5-~YOEfn${(3&y+==u%}|r!Ool<<9>{F(1u2JY63EQA~>4rYW;q za?UsTP@^~0j(AyCA&o7eBr?SgOVz#Ls2X!Vk#Y6Hf@QM?m^l)YEQ5;lQ%3+Ya{jZb zL;W?u?Iz+0-O(KFl#$@wO4Fqd55F8Y9Egna%44?WURU^8#70urN{}_ z!C-MoNlB?U;3@HQ^fgGS2c(Q^u*5HiQXvxskw;2A9v=@lQrjQ!dORg1J_IG?n@`Kb z7YG)6iUV>ScPYnl`-(a2s?_H#Df4mYw!fszQ{uI4F)xfOXYNebj>fDIh}ab(+{+fJ zA>Yxm)vyjCwGs0--aN$-)l6xg6K?SkG)o`w97GiM8J20>rw~qTt(z?iWE?Y`#wXgB zBM4LdkFql83+0s)Dn_&7TFf>Rn9`F*lzDZTolya+wvAjCDRPwIQh2d4n{AbbI~tr= zbD~`5=00b4hlnS&>@Ma)E- zaXQSc&1*Y4RGae~#mb;8Gj>oAnXU~}!%s4jy2vtn1*e}b_V zf5<77ViuHnF%fS67Zy;X3@Z1!S%>Gu0da&z_O%CktfTCpmnAZ*seP=ChyiRIBCA}O z4rM1ao!xc1kNisI=kvkx`2amTpd2&hCn={nQ+4RPCCSOSZ?|U*N zM-$6zA|@0u<$68EWr5N_UK=r8)=Y`~A{qkqq8^UQLGX#;^i5m(GOkZj!%;)et;9_G z*yUSocqFBXt`t*OC9@an&AdUx1tVz~UeHcXgxv}C%Gl&6Xp-VK0;4ok?9`s98XeN=D3k%alJ@&>Rwhv6Sx8I6g zJo9ODJ8js-GxkJJ)ar$}aH~nc^073vcwb9vV_0H&Ql)4E9G)Hw$XTm?FH$dOvU-B0WdT1&tAD24e8FH4 z(Kvt=#4Bbz57xe7pATsmffqX-n;_%X$4RbcXo%9X@jkKX%${M9v7YCSe>Zk1s78j> zHUuE9cTvSi=UU8cj1smZYvH={2@^1;?>ceK!b`{NO8kIo%=V438nX<^GJ3!PBXkm# zcMVn81_qfv?2FyZ}8+mKWlz zCsiLYFA^ESH@bpZ1`>AFfwhd%Jh3xgQdF$~srUX_|0^*yvN^XZol+V(py6+2R*)?+2Ri_YKUU;a0_9kL zg}@kL{LVsPzL#;;ku7B)*ALIiwa-40Z%NQ}~+*Ui+jh{zJ%;RZ5X%s~A=2PqqDvn8;*PQgH&RW&au7h6c&JNNp0V!eBow zS+Q1I#N20X5#I-WvU;Ux)P$m%@^AKx@GUu6Os6(%Lq)7}#=}ny{nF5{AVTm}Ms`tI zKO)r($a_WtBYSfp(EX$;cF zU?ZO|zZh{-hm(AiGUk!t)v|r4`qkPtji?N+_9ny5ZZXhB&B$gF2>0 zF;%A>B34g{VYW9py7a9!oA(z!Z5&`8t7gesMAf+YN9GZ|zMAsVYM);w3yZP8ReJ&@ z71jQlGGCQ9=q>YB1uIKvHjH#Q{EG~OQHK^fRZ4jjxP3PhU0Bt9-0As4U}H z3x9Q`ubdrf%gahD%YD9z>aq$?z+YNcUE!@P^;WZ>z*AmTT3k}iVuFeiPBbhp4OXxm zAXrmc?5*~c`2xkha(`JM;PKV?0#!^xVBMRjaAnQi_q%zBp5{NbmDfBEU*&pi6X@FzzOa&qyB5v5{^YzL-C!rI25LbH>$ zK8iZD$QFuXiIHOSiBuZ?3x>_ZZA>=UXYm%} zhR79v}90F~9 zhH{^`+*5(RRP3p)F85WHm-+$~!Jv3W)c7mPDvHZ^Nm+G`*MrwYfLam_t-7+bI*6x) zw2l#-UL@IVNDfP zR~@W`N5LK|nC>9s3g$21u*GGvz$(C!DQ1}Ay=CP!)Lun#mA|T@9M)A4s4gvq4VMOj zzA_voQY_Yn6&DB5YAI4lnYXmOrlN{zl~R8-C9NdO>QXe& z03S%*dMaVf6+wo9n(``7DHId%SC*pfvXsnM>61aCqB_V@uV77;w}ka(C1oWLTB#I< zDIgSIXa|nV@~>c!7W4;MVg?DZ22HePh|uq$eS~a^OYrBSL1_5_%xV&>@o z%fc!76-(9(aDKmhm^WA&pqJ3y2&G;u06e9f@?Ku<_2J+{uc2@GOIc7?hFPK1Qwsgk zB9M@m?&^nns79Wad4gV=ln)b&0;MN^s?z6?D)ITT2?Q7o$%oF$fX$*l2!v|o&3@8hkuSKvHF|l9@YFf(w{Qvx)J1yCL z;A8jREAG8l-3f{APItOHWx6|ccgNl3?$pKIDb?M{@9tFU?li>RsmR@_uRE=`JB{lA zcUsV$mgC<2EBEdX-MgFIJFmNUe&F62c5i;mz4=G(&F9^5J>79h?v8`q9s9UDS#B%c zZ6&*{d)-z?x7%{ZdEId?cibIrYoI$Z-R(|ur+M6QJdbn7Wx3n%kY= z=AU(kWtC=Gsr@Xsmvr&&IJY}4=(ZkkyL(yFC-!n%{oPiI^_@wDgnR;N`n%nO-R^w1 z`wq9;Wqqf1Afd1I=HtBx1Ki1Z?v(qjnY)_^|7K+sU$7A|lAHQX-`1q3b>c?+#|33G>;Qk#eF0BWlr`5P&z16sRoz=MMb*pjfYgXfyrB>ti zC066Mv5~`kH@UWf1hhL{=UI#{KI^!@jvESjsLmO zYW&jztMSi^t;WABvKs$qhSm6A)2+t;o@q6HSZ6gh&9)k^&$1dnm}51D>#fF?HCAKu zYO67{)@uCdRjcurl%aKlZ8LyW>2T6`x_n-*cyx+9BUch<6e42*refRzipF zRzd>L5>g0#2tF&Zti(zT77?A%2$2O3X~P67TCt$R+Te`z$N*e$sX3{?0yD;$3%G ziD|tFNrbyBD1z{Sm6#eJq!4&tD(~w+ULDA@1NkNLERp!GfrNfmVq6}9GRN`E8e+v4 z2M9@4e4yBh&nqH;_v=8on~+5qXvO#KO~|(5dzV@9J>m%5ckXA!-%&^)-CcvM_&ehX zDOP-XcS0f|%jz(2u+TUSCNp#ve6kV8l##1kGMZXv-( z7)-d6(1qvZ8z=c@5r_}7bGyq30|?|FM|`)0BJT4EcM&9AXF>+y9>To@I56dQyC^HY z18z(pF5l?3GI?$hc;S|LEjrf ze;(|%60+ehUEv7>bF731Qmq6}(CUy$zv`1@E&uO7SZ?}`JH02N2cb71n?TvqDSJBK zmfnZJcc$}w)UBJkO%4`XNvWk)lKXBe>F#tZsj$D56zpSlO1;nO^nlmubZ>8~Q!vx& zh;hQBMDmC25(dS=gS)|Z@3aznK|_7g;XClR z5_omu04t#|->U!fN7my1`K3GA=S~i|lZ)NSj9G)-$z|^3zV75AcXGZvImeyc+nwCg zot))P>fuhxaVPb1C-rkD6}pr9x|4dllgivlgWXBR?xYfTlEN{MmbK_Fjn=Hc{McIhoAbo|4&k2&Z&?kW zOSTq$F3DPa{SV~%XKT^*|FD+-&+n~Szy1kaAl~h#6Xzwly#w5Fu!){@-7Hu@4`2_5 zmV1({mC&=BmEwc#(XEnu4zQ92^|6w= zGK36-jQc~XiG8hv-Va#GX^-_q)&j{<18!b&hA!XKgcN;l6YW< zmDmwhozNXNoJd#bMIL-#elOxf;$28Tm@-0ADR)zLNI3bq?}9y&CMBiJO3v|7N4>2i4|(Sm z^FCN{H}dM%i8y_&4AY(N)E$xGW85gO7`@zk~QF6d|__xD40eCM5f zU-x@?X9p|E2dhlTp>3&`1cveKfzT>q-(bq$2XWx8G-w~OsT-og{dZZ(+1;soSZm$` zl#zBVDyGhZH4uZ|WRuj7C!_Is1Zt6QL!%9jU2y3Sf@975})22Dpa|*-v1B0M@@+;~9 zZP2cWh{-*nuWt81-_X$m-K=CEBJ16JLo#(L;%7%h6Z%m~frvb`Lo(&{BE}TaPQ7@K zpEggUuOPxDF{~tYyxU3$K>uB7<1UD%9`dug($^T``oT|9X|GiJF``WZ{2-wpY#i}6 zwMz!y$u|~Ik3IVnX!o36R*KZE2YU4i9R%q&wBh}kggdQd`d4xRbW;c)DD+v$18KLx zSyr;2-wGUPakd#K6)$z=M*Z;at-9K-B8Yw-zDB<#)AG??n{M=NpRx&=fG6dLF`X3GYZ5(vv!%FDFsw zsnA>+b(2P&Scr-Vh@G9Om(I}Ey#?^6Tq~LKB^M=AAJBe&BKPo-f*`ag{D$ZKsha`N z1QT`g*N|0_ZB6@r8FMczRLK z8CF6u{Q@;5Ar(~&5ho#$Hb7-d>i}Qvhggb=kbvlsaL>Kg+{TXxtyWsu9fUYSCo8=y zhtPxj0zwg?AEA^Gu+oCbgmgkDLaKFXN*bXPp(8V-PrMOxhswhPS5;;+=yM3HKAob5Li3 zm*6LO2!jbeLJ9d0Z(s+jWB(q6o>s@QWMFS#DzFzJpZl(a4DS09N(lkN0Kz?lfrMg0 zA3^~khcJXNh>%ApvM$y3vC=79dcVHZXOJ*}FvLpFOSjTS1-FB|8;&v^oW#oIaM-DG7l$y_?n1 z$J+-a@pirHz4#)uv7N6|t-f}^Kr zqfm8)GjxSRwk8TC@ z{H|~TBqRji66TWny=$Q#?HsMImtj285)0F+L<9xYNr}*b@Rs6NBl6 z@hC0*P|EY~wK`;>)Muk;d%9<3U1%uJx-iS<&$=)p>%w;L1n-A4W);iD=a;+D7v_w8 z^LXQ#@wHXoKlkCmtsfrT_QH#kroEhXp+4)voU9A;vM%h%x-c{A!u+u>U1%VWizf$= z=Y<(R+xl+ZmF-uLTy3~|!%J~KehY% zsWp1tc>QeM^>fFspIxBWrFvbZ*R^_`e*N6h>t_#MKYK{8N3Necbp0F;6_uOAiXN6< zLCy8tGD&-GonANUb(>yy>vaivzJ2KJBX6I(JmYfR<$0Gke|K9&RrL?oj~YE@-1r}D zo;Z2R`6cI7>-7?R1V5pe5FnHgN(p6z zU|z1zlNX!$pg|w*UVq`l`Q7Kw{^*VK(+jhj>edimZK`W%s+-qTcerV0L+%H&rsr{~ zlgmuG%$7^NT;|ASu3YBJWr184$z`!zmdItPT$bhKesq9bep$Dg%ls`|)}P_By@AVt z6?wTI*X`l5au%1@NcQo89b8Tx$;}KPBzV)qcP`d&RP=kpSei?w7@eL zYmsKz%@@tVv?kA&=o_g$%C^`W-AR2TX4UeL{&14&lNLrPw^ z-Hs{INVr@s{2GL>&W+vsN7?j7{5Fg5?`w)$+Or!9Sdn$*U6;829MFsMN%s1$jBy_h! znwD}(T2D*eNiV@i@Dqv&0YV9(lu$+p61*BwVtOU2SK@jlvR7hzCAwGQdnJKaQuri= zPxF=(K1tz|6jkNrQzlHESXni0^pw!a!{LS{;njO#3ZX6A!+SS}S1$@5It>pDomt#` z=2ciAr#O_et$6vEig6XAD#oyiVq(qsnsKAXjC$c)Rr-CC<2Vxp?8!K7yZ6Ob8H42&IHF zLXZIW<03KPeq1E3S0Z~QwpXHiCB9b@cqN5TQus7)N#T=RrAe;2t$lCZd+XoZ@!qcYcD^@* zQ1{->chATj*ShzX5NpZ1CkSWWTS{0)SWZ|$SV>q#SWQ?%c$Ki0u#WH=VFO_!VKZS1 zVH;sPVFy8S-$hvY?n&}}gLH2Y=M5yGbyp8wJ$?1uwdq$+U47%~$!lA#ZM!z>+RSV9 z*XCSXcx}P8Mb{dxt-iMO+RkhHuN}U2@Y<eRT%8&miX+1u=U*_+eERxG&xh#>(Qethpa*%L{)~Kfy4iXO00`*sRalecEUEJ^HemD2Kr7V1D zJ$ct(IRr+TxSz+plzAI9x{WgKAnYO>rBt&hXFXvKrIGUQCLAUlA*Uleoi2sqN~yR~ zEZSl&VIJ);moSfZm`j*Ps=0)Dq?${Rub!)4z6U6^un$Nr(8hBK^Jv?-1me?{M`+_C zlijFv8f6h`Q<~Fzp&y|;o^XW@xk6id51o;#Tq%#H5=s*~ladIf zanBX%BaWdvr9AE_hhC+;QUjDo<3Nv6TFMVS5|kp%Ju#t5?x9QjD)k}va+Ug_Bub$| zkEB(~gvPXtauxa`f3ADTNoY_gP-4oP^s6*Ki7Dll=RDkMcTt;;uI6z4Z7wR~x{k_W51DOs-i@CRwD= zSXq&1G*}j8nWa(YTsa6A<&F9MdklI0AO7)cU+>wwZ^6L9f7dJLb9A5i+MT2`NmrV# zU255tjaN2Z*?eWom92Ep?G$n6m0ik6VUrXSMnTs;OywS>YL8zz0lA&x!|SLNsb1;4 z4O&r~D8y#MRw+z&pQ6A2_WyZv)0>;$-1FwPH@CjEb{A-On&u=-u>HLPqwT~L%&^Le-fO$>QC9w#%Gh4^GxrZ=j0oCzW;j%e{>w1$I*+&FmRl@c>3a*i)SyM zyZFYXnU`i=ntiF^!-hE@Hq>2O|Mr2m559f)?W1oWd;9p?C*D5w_UX6Jy#2=I>6d3- zo^^Tl<@(EWE;n4Be|h2MMIXI7_a|#Eue!YYCp+KS|C7b>FUt?+=a%Q?R^;VY=H*u9ba;N3xzLb~ya$fGY z@|vcvsBN0Qa@x0Em|9ykapKfzl?Z@U<0e(T*fisG(~S9|VaVUn@Z9;Ky|088FF?6L zRS#`>wRQXE(3YLer?!MwZ)lmdF}!L`Xzs$$xdoy6h2d2PLJjL%j@GrTc)j(|&d}xq zp(Q87XI6ygtqd<*iMo%Te`ChH(D7}}C-#OG9}X{=(|Ytkc<%C+Z8KVSo(e5#2%nmc z?vA$Gvf^-P?xE1CV>f0mq_E+2dnhh9NT3~KM`|KwO$L0wcnwyrjuh?7G;@0hU2WNGq1g*VvsZ*> zZwbvl-n8swX!iC{{VSpREzPI--aVoEqoFxRLk%;VmcJfqI31c>ADVl-X~oLW{5hfd zb3^mDgyx?LEtnlzP#;?GN@(HS(88smg|CJd9u6%$8(R2A%Zvk|#q&aoUk@$b9a?-a zw4^@78<(62Eo}%b-4R;0DYX1pXvLDy%H^Szn?tK+g{bOPyF;rEhhC`*ty>jZT_0Na zN@&f}(3&HmhB=|l`$DfT3cY?Lv|(ZBwfUh9he8_;hBoa9Z9Wv*d_1(JKD1?1XzQlX z_C28;YePG>hqkW{?cCP1c3)^uLuk*r(EgdB{i{R!w}KE0+IS{( z>Okn!;n1mLq0{q1r_Z%E%m|&?)UH;2x>8hT@4=#5uGZ=4L*&1l+k zs%gu*aNUaV%sJuN4dK}f!m}5JXP*k!FACQm3fFH5*Y6L{c`ZEWPIl) zPk80x@X9^mS2l;&o(Qj*+q7p*_|>INdm5T{Zwb{e3)Qa-ubUozbzN)S-sZ(?!s}iM zuUi{lw=ul_web4oO?&5tUq2OoZFYF$vhb$a;Z2Le8(t4@S<$p_eRykKc-!Xi_Fds! zJHoryHXT?U-m@{hcSX~|Md7`N!uwti?>iOVcRCDu=#B8C}mq zrE^-Au4`F(uw~iomgV!B&a7=&v883jxt5g&T2?Por{|r>RR^K zx9p$W+_1gn@UrH48(I$UXgR#O<;e7wqnn!-%xF12r{zRl%gNcz3yw4|T+?#)RLi-Q zEpMD^oj$vD#)j4z2bveJY^^)gI%{V0lKHK(&V_azZEcvZLRaSwk}xTx^Q#rqP?vPxSnlYyuEelE3FHcw=SO7x_CkB;&sg{ zcDF7+*t-01>xxyaE4MVST;IHMWAn=0t!tLFu36W*W_#+WBw~Q=C8gn_sosOCvPlSd1KZ5=51V$H*eo|W9`h)u|qf3ZEoJVuX*RO8?SF@ z-gW54hWR%(?7XpgQ}dp~H@57&v2FW}?R7V{pKIQ~=*FI9Hx5p}arkWW;YH1d_ue?V zz4`Eo8>db;A6<3h^vN5i&)qmP`^K5YH_oiOaptsZsO93{KLO)_-vPRTZvy3q?*fy6 z-vg!qzwa7m^#T6SHC)sE2q@{!10`J}P~yMkdQ^YE0F>W<43yt50_FEht|vAB%dV$1 z-a9~v_Y)P`aLbKYE9P)lyo3$feY*b-UpZsF0iNT5lz?2^@742=!y!B%cVU8=7CE&`+^JX2QK;M zg9|JGmvR+?3oHVc_jp`i(EN&BFDeYU9@F=gxW*_fbv>^28FY$NY^BV&w@+7&w&g461e31WpIICagES;Uv!{31m{5rU#`+IPK&x1?4 zZ-5Idca7BN6|Tt&E5YS?6}Z4^`lHnU3(&E^(ctnOFMf z1GvC%gG+h;5nSN^piW5U|Ib|irSM&Fp@;Xt1zrJ{cvry%UIVAw$Gs0O@aL}YX}W)LH7opu znSYRu3Ebs>9mHPaM@(TQA+((-3jrVK!GKIgPKS;XYlCPBa_v9<^574ia?@!RLz(2dbuh0L(6;k-0luP3M7v&Q8 z->xeP|Kj?SLg8zjTrQffT%qu_&fvn=1X}P7`8^)KA+Q6uJWl`@m`FRw^Ca3qU^2Ko zPXQO0%6H21G`>?{I=DRV2rjS_{X?GLLH`ig8C;&<2`=z1+EJe0O*;y_2V9Dd9HV*{@s0D ze^l7d^)rR}u0JR&p!|}qkn#&Gak=y9`j1n7nS1^Va@0{yNXQqR_GYpKF|Yo)>l z>lKA_tu+egS+6TxU~N^n$l9rpbv_E05RZOk?c#TVyRC!ze2;Zh;a=;w!hP0Bh5N0y z6dnMV_>I>06~1M?q0cXXOSyh*omY6#VxAzx@wE8+G+uf<{?$D12rlq`e4Pab;{*Ep(?Ci0MZAI}pAqrTDgLGS{)+z{@#TGA zj~}M#{yu)Pw0Hc}cy0IiY4P|Qf`5l4BLd%y#}^U!c0B%;z{}v0|2yCUe?qzC`MbPV z;Ct~`^!c^;s|w#IKZ*Bq@)P*a@%Y37|0N!uYTz&U4%#pNL&_zv30$5xg9{9?Y)W7_ z9{*KfOMI)skK%7A{AE18fjs{>{t1P@qJAWwU-Nwee*-SR{}x=}zk&b736Ci31^z5}PQn)y@15{7h5wZB z9ffZuyrb|Z$+^h`T`rucJPN(Zeuc%!KTJNy^S6NS0WYLJocf&0mHG&9Fz_+pmw=C_ zE=%S2)D@{eNF{!1W9knTz6CDNFQk1wjdW>`q`i^$GvGg^{XpRlf$xI<2>2$jk^l1h zTj`%qC;#*>q(7|iF>v0K{&@P;^hV${;3eSC`7iiCgZ~2fFL&hJQRs5r(YsrRZUL98 zTSB))g-P9#6{d7cRhZT-U17&=ofY2Mt-rzn-3BTg)NQcB&vhH3aA>y&6%K=6;UcsCV&e}bp251J0r=}qA(e`PSU3!*9lAomvm|1 z0!8kVbRzc&>;&Eicn7$^&fo>WJHZ9s6?dmazsa~8e^P<>fXn;3fD05kP~wRkC{W}; z4^ZSlf!*A>(mygDaOWxP?)tozE5lWzP~<@9H6x4hMqm&4sgye#ekxGpKqZur9U;z0~o|hp12`tSh(Q=n%uoMS8$nO%bKfenc050`15M1CO*Ax1l!LD%% zhk(m>ehysVP;kljL2!Y?7=PvYLyW%yhl9)W&w~qm7+l`-2)MvU!R0+)02lZe%?@IkoN8S}U16=C84qV_&aA~Jm-~wlZ%Xig-3!DQk`80qFoa_3s zKA-1$N#T5ONw)x8;6mgFNw*03LEvKKWQ4PfCCJGFmx4<^%fJOL2bX$U0WNSQ{Yu~} z>Pzx_h4&)YXRIbafotOaujacJz9#86z}EzB1WyKT0vEX1^`?H`7T1Rgw}Q*}Zvz*& z9bDq=02jCuoF6iFfeYN7u|dnbCu5_+z4*UNeeJ{dUEqFj$>#vLz=PnD&mnMuhruPE zBj5s$lArLYW8^3BIDD)p@C1BJ;7M>vcM4qKX>dt*23+7-m&#EY=UgA@``-YU_x=;O z!0&)dx;McEeivNQeGgpV_cKoDcl;pZq{1JnQoSq3beqXyUciSfgQjf z1SWtBOhi7H^hrQTpUj=4OJQw>z*KNamj*5{9bD3N1Q*x|Ib71;0hIKexs!BvW_DJ1 z7r3Om8(iQ$=m8S{KA^8(;T_L!@BJ>DJ?*U4BFL#pO zm+4jL2bXlk-~t2SlCA_?U@7{Gq%Q+X`XG0bzJF#=;Q(++HxOLlAaF@H7+l~G^cG3~ zIiRE;%AKTtFmtHFVc?SPA#j1i(WfN-BS4A&D0dS73z=#c&3p`8;yn&7@Co!GiT^ZE z;(w7liT_OI7Zr{Gmv|$=1wIQd<$4ZW;Fr+LB;8jsm5wvN29$VT2g>iyqi0F}-vCOy za_%Jmip+9_mEaP;3S3|{xTLEA7dQ%iP}08ul=P#ylk_iUs$Dd547j8l3odXRxTG5o zE^q?SW1w-x@l$4?br?D3AmOWBsd?D*^?g~{2e3e&QC zD(sb=qp&w?b0yx;>|qKY${w!p;p|5gKI$5z@Eh6X3M;aoP&kgQB_!Q=*I^gqQ1EYg z{z36?0|oy_&*v3i;aRWnHP3E^dpvs;?(^(dc))W|;UUjqg-1L`6&~~aKeFy2IFq(* z18{6(f{AV0w(aDJZJQI@wr$(C&53RP^S#@@H%Fa|uIf!!-PISm^*`Gf+W)hIq4Phx z7`p$nhoSdB`xyHFbAVy!KZhAc{&SRJ>_5jDX8&{UpZ|NVd4`4m^5Q@LmzNlp|H~`? z{9j&WSo<%p|MP!&gJJW(y!FrjJHyw1ej}wL zbpQb)Wgun#XBJZNe^w$hB9j9FBhw)>{U<%DI4a-2vX}Hf)&D7j){ge?KD5q%*8Q{V zpWXlLL2pB^{Z|g_KyUxgPApa|hJWR+fA7Hmzj)BfF_#$rdH}av>3=47xFC@lx=}lQ zc#NpB7rW}mVZx{-&!{WkZ|fXkbsV)xMUp-cZioFjT?o+)BgOo22$5acMp_aB3h@bK zF6uwDtXPjji~ctU=)}!6%X`Gs`CPo;16Y(`qkpv{zL;F4LGPgjOF;N2!{Uz6hEa4I zb|>$@kc;E5`uPa(fs%S9uS@Hi4Bd9CHuZJ>Y2mj_B)cdL8pqd>sl)yTKvj$LX~dk- zVKYJnxA_ri@`V7IXm?ek6e`~h~2{=omN$Cz*%9`2|gJ*sh3nv9?9IN z(275XzsDZqDa%_8qy zqgq{vp^cpav+Tr1sG=zii7I{Mwn$*OnAnW=Q>{#$MTwkibR)Aw@~Hm;DPMN4A)%2_Xx^|GKi(vfhwD=&Cf6=POrM;2>4?jl%Ls(ICWQVDc1CIXh#M`N`=bnDNc zy@$rEU*zf5igzfgJ9Fh#tA`ZOP#47^fj$%3N?l|kUhVNNy8a1ix~0eElm=Z zRAPn3$0=5*kwknPFy)%F?e8u?4F}c3&k-NaHMDS?>1IOfqu(sWeW8-!;0|^qY6wH- zbH2ChXPXRpl--P76^&l>fbSHSWqcQB&le__L=6aW#U2Cr9p@w>lJXi?ERd=bCLdrY3d&KV?AiQlLdA!5blUm5f^G|f991wKYgxgNZjqzi(hdm62 z5P|>42&@Tgq`wv2H^0kXpoZjA5REte%`)-7(~m#Tib^JbfzrzFyZcZRSZS<5d$t?| zje2gwAytBd8{meQE8}b(p5;kg=u|HZn5q^M*SjCuZyFcQyE-{_Piq*R!v4_ozlmau zK12v@S*HxWF5sO(nDBlia`bb9&P-$|%NL&I)oNpkeENI>k+HHVpHd!_bb^)Q_az$_ z{&v5+wajp}B=j7+3ru-nxn zFd%+z?|XP3Y+nzmfwliqhEC1rF-Y8`Vc(g7n_tUXfy(C7(5(M){rs~1_|HMZQuaN7 zr!%Op8u51#WtATvh>K^TpZNIQ@}E9e20MCIdF(9)!$Z(>OkA_GvVTuSn+qOW5I`YH$17zhbFQ@1!T^SK}_iaGR# zAPyVWjBNo07kHaoF>2hm$yES4<0ls9!yOZIdh#J1m9WW$kXZ_aVM^2Yc(k+v=c6!+ z(NxMcW9ztP7zB!W`it6Nbdnd8KmHX{^$~!>rGCwmZ2aWo*)QZ`coZAy<6gC_LOLDO zp9FUwZ8jKcIKnV8oKx#bq+H+Y>RS(p<`2Nkc$? zs;Qi5-*nQlNmtKbBLSW(rRVOT9RrlyUMfj~*FZw^&GaBWj6lc9bTh3G^P@nVL_97R z;O)j~krMIyfSZa6Esb5cF^#DnhomXrM7~Km|{46P}E~+M3vQE63PZX(9K# zA|!u%$zTK#0y9dg6)Mkw$zdavAH%BlmV4jk?CQp%PDyr*l4UU(e8y@U?w^5Bf6vw* z7I(IXbGs&v`V|)&vWnNR0H8~PMUMB8UlwwowfB7+LYM*SlI?ct^GzfzT`UH1A9U%X zUvxX6JRVguzIH3qUn4$bWVax|!}%FH#EWsaref47Nk`A0Vip$fu{IJm#gpx8BOeOr zS`Tr2LcG2-DcgO?`Mlndx?enXUrr~*pV?sptzeK9;KWb(8Z*zzdb05slG&WfO9@_V z-s2>FWEms{4g(9EQuE=|(iaBc{&d0wJ88)4{+S*w=ao6fmVvRVvo z&A_OT`JP92XDaqo<(*L&sMGD7^ZA+S&&|55!e9wH!JazDwTkk)fW6K4>Nj^);6(aV zGL6NV&&GiE2SZ!W;w$2=@X4HDN(5$y+chJ|@^e>3VT2qavSbF6$;TT(!wT&)M$o+0 z`Jj^E6_2k}mbwp`B_+IuHGR^AR-`#+lCI=%IJq6Jf{X&(rtEU*VO1BRtmWN>pe49n zmtE`diguvtK-j_Jfe>Pf8)&kKSM$!HI8O!!yUZ0|@d|6<0s`->Kwxe$m&!4?EPPZ` z6Z7^*=dx(bf*-2W6xjP0KlkX(2-Tl^yeGz|H-3SFC#SB@y=9iVcgiWqU@{=Qku)zJ8aH9uVk-3TlKIlyW15hg%ZyHj??EHrrx<@7 zI2<73^}D$|$L2W;_Ub~xin$gG%yjrmcJ!0aPJ%5+TKFF^(a#$np6&t@5iLzG1?kM$ z@@RAS0bP7H2J|&pteS0K@(mosWw1f%a5ON#HX&eT4#!dYSH+Bo0x|;7@`mUPZQMPh zxvB_ys^6IzXgjn5FbWt=o{Bbw$g>GL5i3*=&uBKQ_os}SN#yO^xSDdQ%uE&Wvq$CK z1>8HII-Osvu>%_gA3=6!mb?mfspQ()<=%&Prdw}UCt>3+@wDh91WsCagTsGGGv7pU zq=YhK%;28h60nnHC7nE9P9{FpeyiyOM;(6wtNlcxHBQlC2p%}QM`XwVe&2SPr^%@3 z9WobuteK3CODC$}xxh9$n56V$jFraE(tizwjn!}YcG;2q{GQVMl4l@C*2p@Jmem;~ zOve{!|1nwvSoqV?NuX)p)viW9SfMwH)d%2C9_jr?FQ-@$tjhPwu?YXlisg#^8|j=k z-cIR+Z%9|3jKhh`UWOz~XoYvo+9WQ?^`yE$9Y%n!1;*YF2kx2q_)9vWHp%q-iL4`n z&oal7`uO>$!$pPw z@#TWV<> z1o%>ZNyj|5TH&X3YI~LMEA`(Yb^%fB?J@9z42^_qnQ(R4Fb&8*Fn>|IP@NgBXu80Y zyX=8HgyHpB?r%i1RkaOW0Cqf;wl(jgq5;Lu+EB`y;MA71+`&FfA06Xg`|Fd6Z}VEa z@j4JS^r=U8D)1!RGD2owe_dIpmApp!4q>eZ?%4JRMapX|M*m(0DnK0}^U=589AO*X zk;@B8V~DZsUzGXRS0x;`O%gs?8+s{o!Rtc_Mb&jepJaf8HC6p|5Q-6&@xB6GU>Sd} z)08Qrku>Qu$JA*}P3a9+Ib}J!KhTR<_sBO^47P!ZVPqxBmY$#TY@DWE@^tFaTSIY% zAICKPX>6<6PvQG9gPtyBwDRol%Tm-bP9}9OXcF&vD~hMHOj$CcL~%J9#IR?KdLerZ z;p=^wv4d95^2d}pqQ7G^sVB=llQjTyWGrIqTjluyPvtm@HX-_{CXDhq#mNI^#OSnfO^PO`1;pn$9#|$DwQR9R(FQ41dH_TZL<68eRPLd z3xN1`fBJyWl6|ithOU;oiDkj}r%oX9=vqL;g&*Yh=D?^=cNf<0BUd(tlUKMp3 zVhOPFNXBayjoAB|oISV&Q>lnx1zrHs2i){x$$S(I-b6u0(HotTBjw{5)0i6 z*C6^c>m>rR!1+3?T;Wc?b{7gxx>$|mc(94NPipxS!K~O;J)# zi#rbcNRrFbPWctqdX%fyf+AJSBLtJ}O`-drzEicu@pyg}o5j~eVKbs`(cf=zi?#TXb4nXyNBo)pac&e(;c`tv2%OMzbSW?H%wMlHsb?+*kQu;TtH}# z8clH^{nPWEw^PwjaWtryK=A!A%i9mSL6nPKU8*OpN^y72`?VZU$cI2dTPzuCzICjjH|`dQ%c! z{nU#%@IZB0kHnWk8N{vt5GLjf(M;fu&;+D4wkwI2qO~zGJ*RoYYy7O(AGCmgANAIw zJP^PFtonA6|E>uit?VG}qS}s{NbNT!kp~Fi)I7OyOQVQF$Goioqb%9by0=VlYd&ql zS%A{32X(JeGjJ$LO-Y`HWSOq%GS_=UAv}fwSH+q!$R0#&sxoKMsteq23vR$5|DC-J zB73n4`yovi^>ysOP(PrmvLAx;!xgEOqV*|+x za0ARt`7Uc*{yJeU3daM-2VPSJU#3`c39?*54GRD>FxTup6m_Jgc(#NbC^}2Rle!1g z7~Q#|>!hQDileJL^ZvY2VP)>qK%O`HhNMGhh8g~D8-^}CUm?2O#bOzpJ;-Z*%WE{* z%xdYiPnK2US3|#aVW9WlV^6(0-cue0qkjBrX}tt-0we@YRFzwZYEqRIVJB zY004*Qp18w2gtk%+wUn`(_Fd=>Wj>yH{VC3RV0*6V#uWneyjA3s12&r>s?|jw50L_ z?wEC4ILxaI>Vi=tcd_C^2xV?ddQ!->DySJDoqPUj#3?_g!?n*fvv0PvX$I=xn^7RXCy)bzlEX2G#u62^T6WuC*4lzTU+-XXsleh(5bN`2 zECHu1sxTf~oXa7HQ`Cz4X%T#oWU@*r4TCcl;W=d2AZ`jkqCMkrp2=rt$ES<|rS?#yl^W}WWGNV-5XVJMdA5i9` zSqW|RY!hRnET4V)PCog-5 zvfgcm`&Ps9rOE}fD8-rV^MeN1kWKho6b|xe^GNi(7II~Sgtl3%f47oVt+OREr8eCi zXG@)N&SINyF?g+7-%R=I%(bLnrU#}q$IXiTAJmsRZy1Z{r$#DETLCOKc;v za$XFx;5?rIxsE)=v*7=&CvCoCh9Yw$g^p!=9(lq+-84W!t$JyC=VrYSpgg^1_YWHl zk-P66{0TspDH|SqnsoWm7O<1c=N~&|o!CnTLBzpr!`)uLepPnZp_{x=Lav7HXyFgu zN3RpCxIK~Ct}I}F<8OOL6%O)qbKyeXEb`f<5S}k~bJW^*fi~Se%S&x))PKR^?Q@v*j1%3$1U3rdh+p6cD zt1Cw0Y548%w>Lbp_;ypZp2Sf*iXg88Y$$Y;ZH*eQlN#v3K6$C0NTpTuNl`w_`1$i= z&fwk5tGFuqUhh^WIy9%w=6nQ|2=Bqoy>Wj#gpXyw;Bhg`RwV6ixk(HRiro=5mNpIm zDAdn1NFp8SXB7`4NJ-OT;`OqC85KMF@!Co1-3JKFG=t)Zbg#fqC7#$Ya8#3zNp)sO=l3%dzuPb4dWOI_U;Uob*VqV zLWI;upuck6K%z`pa&B2Z>Zm*VuwJV(VEC7kZ~opegcVo>Q;FWMc#&roB{uk~S;WYb z76EQ(Y$I)4XsLTc4M-fFw%(9fzGk`AVKtJ52{AXEr|=EH-}N?~fo6)^KKe_0GM(CX zTKGl&P(3xnzMX%#xxRBZuzL0umc7i7hWDC!m(H*HCk`2ydNNgu#tbu zSQ(EKVhfEi+CV+7q2tAkcv9I0l2Pen4fXeszpi(n|Ez%d%arOA833pDmc)uzrqN*thQTy zwuJ9!Wuo)Df^EEPzWtoPHXtt!+6*Uo~miC%>o7Pr$oHhOZz*#N^$C95Xr@;fz?nXhQ&%aG&W&XQfPw3ZAa!do8`s9ym+=b@dmXfI;o-`?zO{Rsq6q<|H#vV#1m|;oFKA&UDN?OS zM?ppg-NKw|nTT*JvKLMz>#zyqq{owbk4}_#k2WEYyk!59I7#hBTEeI;iGhWy7V1R4 z-B)t9y>ruQ?27GdM9_oDi6;#H_C&LEEoJj~>w{N8Oll#`b-BNfaS|DM#3~I}U(XgD zRDgVJ)udSVCv3Qnbl3B3axewfCzPhDiI$pRc5T_@`SL(SL8sg~F|M=Ad+jH1Uha=# zTYhvoirz0%+_0=$k{tD3y3H%LRB{HQd9UQb(EN02z30!0&nqZK`SXnKB|}V6Q;2S^76KdxO7mhJkk!aj0O-T-{LT$+#CZQ(ZXsI4a*;)K?u z^4IX&%hCu7o;=?5(WOprSQ;_M0%+IwpJ7isE><7((Zwi}ue-*biWCriWjwB>tF}WU z@GOV2T1)oaQT?v1g^1}zIG4%XO@4-$I!qCI@E3yhw4ANFA;x+#^%<~N5@2ebeLA_E(4}gu*55`NH75ps=7bdd2U#OqKrKw z90ZQTLm>*uP10|@tje^3c?V-9Yf|Ctoyar|EFda{&N5fqrl7RC_rup4=H+ifj4fv7 zNCs?6Evx1~=+$K~= zaTfF%7t8iiuTRh~oT=n&i5`(WrbW6J>^uarcVAz$ADj7Gbbq57DL6>2{!<1n5TAmz-h{;KYR*SX%6+9+O*Zqs^pDyynHy>qD+dXKRy4yek0 z^6N9Q>hjf6iHMz2{3IJoBDVu-ADRtKFTTZD*gIhW&F?eyIuBR~C)pzH2!7~jo3ETJ zy#OBVshS^9GeSY!rJ~{i966$^%2+?;b?neT0b20HP;Y9IT)Wa2k%giZ&o$)r|)=N+V@xQt%C@xJ6I5W=UH`%#2Ox-}1@=|U}86LnTG4NPQ zls0XGmv9B%R6l+VKZXqK*>@htOuwDQeVXM00^nb*ZfkxmA<%k;HDBV?={rGd<3o&8 zObGtc#zUGNV*M8MHQ{>TK=_y zmZQRt&rRx}%6V3Z?Q*Lx6gy1Fi^kv^QAf9aN;th(6R_8`?%ZW zJ=UVl6Qq=bU1ZQ^EW53MAH7I88W~1wjs5`V4-9io*J5YmGZhC#>59jT8quP;9K~NO zS#BW$<@yJX-xXLppf4i911)n-g(t>Y5&(cHi6JRFySGNV5jr#A+fB)iPC>0is@mDX z0R(TYkDLzHo6!?F#l^EEo!7C1BK#Jk5J_1gK&{lF+`6UKP(>^*o2chwDpe12Ahvqj z(g53XHg&Y|*8?L-bUZU~!YBS^>dknAUhg`3MyK_sP;-d8?ks`2vT;vDrTOL3jIM)R zAxRcFnrn&rETEH-YB|OoG>`pJgruyJDM=|2SpZbrkj0r{+X@MPV$F2gIQ^q?xw`vI zD_b^^h&LtmYmWAL2+>H!3PMD| zV1{1mQJm{N3s@@707eHc>pY;NJFPDT{`_ba1ZFu)5a{ns6qq0F66aVSzFOv<@%_d@ zWIc-lCTbVlezASGDPNck>R;Wt{@}GtABnXk(WP|>@S}5t$Z3IKzE?6{O2p~Hqv1j# z2GMo?Qkr*uQ};Zh)Vw7i%I))BPkyGb!~0|8>0p0>17~Q*Z^=lxUq+5r9ZbP1n#DD!o9LX2AHqOMP#dQQ zN#54tohA$-Lkz#HT&!=gY*f}(y0seV2Ga0+V|>tWkrHuSTnyfy$%S8cZn%^3c;;)H zr|K)!DcuxTx|#u*TS{8l(~P$MCGRsK=eRJAo}|H*tnjNdzsU?N0UImrvTwwF2sL>s zm%e^U2y&iWJn7o5Xp0`%H&U@5Bo|mz2*U+>QC;zE$G;R3{$9>=ZPnVx9#qEO%9M4& zP@_Jhj}fKAxYkx4GB(B;|dCP^%4SVoaYA(;KDI%%;# z)Q~NA0N+#w2{u|i`le|_3mdObPo8q0vR|{318|BKp_A1D(IE4tOG|=dD6LHAQ|&VW zHD=j>p_KF@L^&G$ekcdc1*ZntDZpfl9Upe%i<50w_HM0mEaY$xLSt(g^O$&f;)#2V zKyT=MSoB&-QOVt;?l6i#U_k%7GSrjkDG76Cc5Q?g6)BGT2XMT7Keldl82-mKLh%op zxp>`7?E0k1cRq3?{}Wy44!!?nEH8XV1};Z#1`}>BYIJhw@Olj)kp!_#!y!4KlMX^SGAS5Xguzo>Sx1A~y=hu+rY zCM%E78sp)uIqKh=eDmZ<;y)ZP0fsJ1TcP@oJ7wq=dSgn9 z4HqIYcbNrJTWjWDWY2eyXU`9-l%?FJ(x3ZvkQ}(!_Xs?BQT@^sR~WT#VNMn57O?* zQ!3Xa;b2cY2Av>H`xm?=5tPD5cvUbdfyt48X0-%lq^Pc^`A#$?qSSH4uHXtW{tAq8 z8YD7C%6pTic@^b&9FBd;!|ti_XyUdX!1hF94{25(vs%+gXqune=-NTsRi_N88Co^3 zB$?Bcb6RR%^Yx!f9vTw})o*OarPSK43GPX5mwz>A^nZ87E{PslR^3uTpTHG~)XXC1 zc55e2U;NDJW#hqRW==nhri=-ep;hxM$ajm`oVBlPQ$JT7OX~1&x2wtC+~Tk_@?(RP zmzi94MQG(u%8iU{DeFLDt;6@6%Ykth8yLt{u|Y=z=a~p_GhpOT9za!DU$dam`?m^s z!E5UssIdvyW55VmI{zUuGklLN;R|MCWb!h;PAGL@Udyfux6}3or(7BL#Q!fI{vt{d zlAzn{4Qj_=D6K2J=x&oVJAQ_uxFE;}&=|AW$Db49)2b zy@m7O>%qsoEFlpqGJ1vOVq-t#&PZ(rc;8#cStEu)e^L@Zj`wDg1}}s;d`k^>071zn zJ2L{!L~zh9alK1s1FaQ8J2Z(aby`<%m>uf|VzwnzCES+fIh9^-(hW=qEr&uWpt{SM zvfdD)mU2ZUOTIbU)~0og5oS-`UZDgnX`KHC%a&6b5&MSKg`7$pPr&jpKi(9>Tuv|6 zB7a0W(sf6UjPY6%zkGIUVx*(2{(_}Z(RXxjt7YC6fYdzT&_`4?bE3lkWCx8}G~e?U zoqZdhw%5r)bh-&nyviHpl6;|1XrX18+r3uxv~ua`F9MTa)@HVjs@H7u7jzPlCjPz` zQf4WXvQho^&-wy%lA3fta^^9I6aMTY)p2+sD^;kpnD(8Y+$RxWp~W=ZGr>>z6qqLT zRDKZFpES4vG4FpUU{at1g{NKyutGn%{kU9h*IR%qB2~uL-+JH7W|c8s@gzeF#9!#Y zDl%3}T!n@H&gH>tKs?)njLpygEba8C{sL+?n@%S8;~;o}OkMn26s|o3DH6_K9Tdw1 zAhJ%<2Fc^@hj<$`N**Px^O9qXRnfY&q`1j_9@C+z;MlIdSa;XK6Zv8SwqN2-cD&?Oi1vJWEWkJ5 z{0;$@eee7So&vCz{ArmBbyov2dfrjlWXeMALv@tAtUI=Z^+2F9@U(;{DbW!j%*edC z-G2qG-^RGO*w`<15>Y0_DvJ1}a7n*N)>0-PL~*+GvGtpiRB76jLhGo}6ET2EJo`QI z9(=rMrGG888Ned$Uxfs2DLlZIaD&XtAbs4zXOwyem-Ce*iLnrUv79E-kDP2x7xlOQ z%k9|BM7{^u`60SgEnu-Y5QSU0=NIHfir-#5DZVp1$28@EEvIqz(57@OSvDDngXRgi zQO3*&r&YKpNF$O1iC&*05NdfDmQwIQE)_z36nfZ5{2iy@^T?!HV9TPrSfMW?)OymB z)E9644RjNv*5dK(N%A!RDZ?KVutW+_Y z>3AD9^s$4Z+ro|Wo&tfOJwXyp&tD(npd@E!F8@ZbMG%62DkR(;V;Rn4Npo}8-!qNU zDee@WVRQ(LYB-@eJZvQRcBZ>TW6uN5`tDuLxo7YE`sJGs z7^1ss98Uz=3%UNf#6EyT2Q%e?!_;#VZsy@o#YLObm!1uGZ6E#B-86eL;s*Ss$PDU% z2P^RzahnPtLy{%_2&1+LWYwH|XU(=v`wg!4%_BIfL~Qk_cQwNkqEy61_{*;KfPBOb z&85CmkxM;(I8!ZeSvZh2#|F4G!4$Vj4Xz4aA6|2#nVBO!-Z@|g)E%O1&ZSot<-W<} z1MYQ-2_$7ok7ugqly*vF%M;{1E`rZ zrMi}bfT&|l+TU_Zp%#a?%Zp`?hqR@N&W2Sxt0fE0w-&J<)%^v^!winqsR$Ax6fUR+ z(?esXn?757it8^euvSgAW?1190j@w#516=pFe*(FSCi{;sqDRJsbW28ez6v%Qj>|r z#hh=vZ3q>H9vjaCbu8U&zpd7oSXmzqS97VSBU;x{=p^whcgV(cU2WN(KsFCsH8>8^ zLxIta2X3m3nU_Y0~ywaDO=OtL_X&48d}h2vCGk6xAWT9~kS(|W-<6DBg^8Dk@XrZDhvu-j* z{jbmfG7TgNu2eRlJV1Awk!fZjRGA6w&5n=_mX%SGJeY-~jGFGY_%-sg8$;-VBp1CG zT0_F5h2$Rz098w;EmR?aFWs1Du3D~4cMM_5>-8<}I@7CTQYIU)|u+3y9Uk-V2Z z^I-3#y}rw8RHEBVTd(g{13}f)27VZU49s6XcoP(d=b3z~Q)`KTQ>EwNr`l{3GI_?z zWdb;6Ydn{9PDG_!;DtZQg3mG6(p54c1u3icuos#^*|8P}R6sJx=Mu&zl=>iQA+6Agyl-Q3I@0>EQ^8O zAI9TULFR72%-+E_5%XpGG&!6lIXN+F624`bqIIkH8Y5xD?bhi2A&n;wT7+%otK7I` z$<^lVs?~AP_q{rd2eHFjQpvTDWRc)Q)Nm7In5-?w7$EHYYBbBB_sh3)DeD(4ASAJ( ziYL02N+Rzu{sz;a8ZkMlUX8si;q3m2<`zJ!kSV(vA`f;D7xQo0h(rvs&HTaG6-UiD zI`xefs9EZdC&nAKREG%0I)C-eX~x;)WY9-4P6@1Vm%7o17`W@1 zK1|U5@yNsGq9FW5z}bzqMW z8r6TOE=Wz0A03={@!%-}h7+mr){Z9aXLO+!5RB|2BqUDUQ4Pm_u7yIQeSJ4))lQks zN!P1)up`GflUR_uJ9vW&%%6B1@#R^=Y#{h;j&aYYY+{oe7D_iUcr>cvU?xbt^#O18 z;YTVH3=9+R;-elm3)#!|Fhiwxq@YAX6vA8J9Zk1Rr$HY;Ez|Q#nb2Xzl6t4TQl|p_ zvMH7srF-X&@Q^o_3K%a6$tZa!%bYm^AessZ!FiO}Hda9N{xVLxjIpyHAsxug=-*Mx zxdT*`Pv_pObD|eZV62wD> zn`Ks+_9OQ^)5xyd;da0^vpPK zRHyNrpACzN>~D!s{T-1jNHA!i=2u8 zG=^l4_;XUCd9`#@lH5MkUG zwmP(j@$Wk<^&vH9qb^Ggzvt?m4B-2WDD(i;?L$)fO1zSkIr6g^a_6|zgVAm9#bdzu zeAe+BVCVL_b$tFNYcTNA4%f9FdvFP&n)pHCW_cL4=Equ6t*1SvnqtwUcm+-+9LYFn zLHGSQ-G7c!5aeg+xZOXGom0xVraj*SN#OeR1d*NwZ5g!)LR`So7SN=8X}&HP?E0{H zc0^8EmpJfoHl^5RbX~A1zYn!z!Fi|}TB8cT*+B*uXfS#(=sZ&xC`Dkj^YuIsK{U!W z!dJ)GW1C%CiZV}Ijn1(L5#iuIn&|L}+nCmqj5D zYhQWhvcylO-Vp>({M&Ov&As+qL42x*;oZVi%S!mTrGlrnLJldM4|`DWGJm9(9op>U zpYFsqG&-%loTVyK?6&^HOeosW{y~6^7ZFP<{?? z@NF<#!Y>v{=Z8cJn0NXL8Yg?;UjX}o(}uzhKjEn`^1-UYtCE<2X--Hw>Ksm?vjeJQ z1CY!`GYe8?leno6(j^s-2}1CdO!!9is{l8s0{I?Tsl(Tsegos1=U_OnI<=tS+IkY| z9tyNy%1=fC5WC>h0a5AVd^DF%X7dE&&1$&y`Q#qi;yz)W3<{QeQx*rdZx<5)LHxLomV#Z6S$s zh7szsK#Q5mX?@PBFr#$?3mB-#AXVYq_UGa6#Ju5rAJo@wN3yui*b_#2I`}kEGb_!t zUY-GLI<8fj93WZD{=V-Z^9>0mqFr9a#zN6}%a#rtR8)ilb zW%+={YG9bqy7zf0h#h=-21uHZ11$HWgUhNSw}$T%D$)B;a_n%4(z8Gqil{X z7{pLZ5pa5E51K07zZ1VXsACApSOsit`u08O3BK&MT-NZS+mn!Pcwa}t$qIBl=JDit zC5W2H``#v*-CRmTPApV3)D|R;HOYGff_eqaBPcf`;=4~CpKkL}-N@|H`{#NhrGoT? zKJMA)sN(oYfVDLWUFx?Nu0NAS;Q@KhxWjSk-8xdA>!d(UpBZEL0My{UhR4NX_DxB> z2Nc7n4N)l@#ICPljEx4@0=Da2aTI%1nF-qG zA9xeR0_tG(B{@%KNn)XsLlZ0Yl?ujIVf0#y!SBhm@0p^H9`W3`jnHOPT1LT-GeeW_ znY$a!^Z1fynNM&}afT`0FGN@XiBZ0qCaX5L-CaJ(C5e!7G@3RQu-luLT+mim_DF+^ z-lv!K#vmxAXDOEhNc;rPE&Pt^7KFRh#PZ~%Ym*J6#>75eA-lez?*7-WN9;#}r;NvD zS{9%B;o+3b3z>^nVI6dAb~iaBhIG1GVO_J0P)c0Kuk?_F|_WhZyhs zYCM8ZK76U%Z1D%MR4RL+($_t>qKn2Bk+1W)z5ag6T&FiZg=_$}csvjvnRj+dmVQ5T zPi;_$G?x%VmJQ>XOu+e-tvX>=7AeJUw;|0$BQo64(!6m_Wejl$G# zB~-B@7OKj5biTB>{$WlO)i}-F<88o$3sElNGscm0IId{Rm;z))cb$8?RGmwAc!=my z?iY6pLHsoXAMhQYa}jZ3ts-@f?7T$HPs%wHZiGb-{FXz}lt1u-p=7*KxZ0o_#0j>@ zke6^h`F)fpP0Bd;n8L`=toQf!=m4=qcEa!MG150?9(M@o7XuM3@wj5iXqu|LPRKUM zCV>yG*dGg8HoWX2*pbu z#hr(kY=Sjda&p-nycGo|o)J*>O-$(d6&~)wL~NB@%a439y3K;;jZhO%$Fp$-_blp| zWfs;H{dQ?!Hk$S1)4j0jc@S~V&V?z4OOknteW6Rne@xxX+K5*Z1dO}R{om9MM6{Hj4B zYbOWXn|q6--CJUV`Rd=a{G;;vcUfCYECugE(7y1Ibp~;N=%2?<@*>J}oX8uWO6H#W zH+>Hbbqa`Ndd!24R8Hg1x;X_85e#;WK|P2S@v%&*O+No={9m=_Gbbl81(Li+@kW&2 zBG)t|TZnvX{N`m(L~5vR@xxk18hRYj@vaF|Q0ACOL` zmPy#ygvb?^<%+jc~C)3H%LA(HX=WFNTI5ja) zWyLp({&5;Jn`5 zCW=Y(F#2+v8pbrymbS^+$&}HifLN&PD|wZf1FuC!%)J?6w;WsG!d8BMR&kNPzzz_} z#9@YS2j|%Yyt-Qmd)8{HlU`W_xu2RO-23XmCUv(9-Rx)6L=BbJz?~PFIh8zYd5q=w ze$IO3X5ECY+L|+eB1JyoN_bc2g(|qkMz9}aH|(xZs<&7NFgwam+`bEN$qIf}f)CR8 z^-t88KE2J+@R znK6u?!4#V&Nr_h@*{O1kMj;U>8s)_Hp3u2w`?xEH_BolV-y)5F=VYvkuwg_{5PLH7 zeM(GDaB*7!{x38%m(UIZ(+EVb4y$^(W9^=hu_){Y#iWD9%Pg1vtkHbG3EK9EalU|;#V+SuOPUT9_98ZNFGmJFB)~B0QsO;EIG5SG3aJkZX3#w zT1>sDr4YH0@Y&jAXbDNuk;=i_XFtA^YK}~H^!2)LuB@OD6w=_vV-?s+3fg~o`Y14f zjF22jB-ICBc}xzvR}y@Jf?=r3Jzr9Ce!bmDq|p_)ll1(Sk1b!R3OgoD3c7y)pkqK$ zp@>FUoN8XeWR=Rx58MxXO;bs|)k%Fbtz^n;)|c}w;zZ9>1$KXO&$RVCs? z@x%sgZmD4oh=<*%Xh+rL2;~(xR23Cowy|>O*9o96Fs#;{Lt|?QVFb*Pl@(EL=9QGH zm9{a}-g#$~Of7l7!iliLXt1H1l789D+iRRQ^;DFlj=t#tHyLanfP)qlI z1-2_bz3-(wV9qV#gLTJnTrBdjpJWq_CbTS3}Q+NuU zye&7`RFsoXeHa@KfV7>?g68OGUKZGz5~*8wf4()B;tyKmdTbJq6)SZz;N_uJ_&}KAhG@L0|LKnow4m>@F80#514i| z^;v*kthd)zBp}J{07+lKsO5$p@Nl$_zy!{i%^f7dO846^=;92_cYbR>e#z=U0y{@<-l2(J7=_ftwhkYY<~amg`qaHZL`}GUzBF!l7*xDja-AXN zfMec%bg49Q<8%`c8#b}~$xrWB^E`7iwda#T9GNWLqH21PaHrDbhj_~*4Z^eu7~MWC zoQ}iSOaQ(Lm#M2;e8&*inAK4OtRSRKI?HB3EGHk{ampJ0g4+*E_1N-xZgiVIS4%d> ze{MifOZ`Sa&~<80%%13Op3!?`(kpRYscMpv8yJp(0OFlk$7me zD)@6DBXX%|XDb7qEh>`FZ+|+-{u$be9j%4Gi zvp7Y8k0dlhAiQK)G_@pk5*@s?)@IXk^%M;V665Qys}`pf?GwIdsoN*c7;i0A2=6B7 z$(G<_E4~IB87#FpJeWl>orR0MdSh%CPqF5E|H_^FlP6kgrZr!sn0*QR9pQu$;3tm2x*oUQyn-`G`VTs&4>UTAXmq)hMG|#>D{Q#HNBNM z1mg{tRfXZf2zFkRH6^LvXM&5PE;3gN#?-a=PJnlLzu>jol)z}}Ghz_bXxT0XeYY(D zvsk>FaXwg`#63OT=V(}7Isrgef{hzIo;*hvF(IqAnS7_Bunh8u_`8R+Fl;~)MLk$wcS+hPpk{LV`~<*Uw&!)pl)Qz@zA;m zcXOpIsS#gvD3*GbhMHJ;^I@N5qDN&4&t%IBI5RNx7!q`f*$?F89sIcMp74+C#IA#2 z#g*Z;ZQh|4+y_>$2Q!y&)WC+pf)r*tOi`>EgP^nKc?n_tAX6g;)RX1Y3mwe5fU5ik zzzP*50MZV|*|Cxfc(2fY4q-9Qseo=ozx7MCAUqub97MyG6&ayz_jk0EKus%KE|*{4 zmR?VlNlRG`hffORsBG_zrbTav;+_+lL&WT003*0 z3P*BKW~wZk^P2A@)9<7&GI%k)C&QF86+N)pDKT;K)q(XVfb>EPF2@(=Hif*+K&lU& zHZ6K(TJ+|0f9k})JFxI+eDjG1w@NL5#Q}KOCNh`w4K_?d=TA@#iC^W^uAaszH{D#B zjv({u4bWV@L-&1zVet?XTK*+J8kC}S=QQdzuE<&0h=wyNj zYD|%{=o?<8PwWFltV`c$c&*B(?z%_IHrm19bqMef0v^k^_5H|)H#$vz41OPU94jXE zL+~xvbZr0|-*%~6v^aC$6a{*DYYIMm&#pPF>fMF1Fkai^X{?+xY5%>}=~+C;>~w;J z3W7I#BWEM}EeAC)I+VD*NRQGGLT>cO>@F5{QHXySDr5A;1q)0aec4|7{mKPpGNUrG zCXBy99qw|H);RhqXfzD>OYJU2`E|X+pC?YV@(roScHfR1unnk;X z%ANoXzfiR6qCL86!3I8jRWiFFTk{cT2QU&Nv6b)e;f>2$$JoxPV+n~Vnw3A4IQ~B3T@php#jJMD-_8xp_s6QWO8~|;U;76weth~OIOg+zC$l^k9Ig7d34x* z=}fbq7?CjvCXRdoBXV;X{fs zecYXB_{$^ZP)8aY2k>&BhnT_rOM!Aeneewf(Fb7z%{)BkA)clRboUL|25%ar4mf zzNy7F9;S;J(sy#-0>;;;*1HS5VUGf-I=Z2@&~EL8ko1^>Zm!lP#)}=m>7pGAD}$iQ z(QtxXw8B@lN^;Oa&P4rjf)lJum{-UMHp?{0;AaE(Ra8a#h{&)4dvdh6#p$FlaeVOb zKHLl^2}`5Cs$SP3GZujW)3e&7G1b8%vDzK z7$o5|q=y;kpWMu|S3f}nh41yDV4;_&%8cITOD`3I(vDbo6QpOXQ)9|$& zuxqYv+4jhuCEi;&@*fR*_AqLLu=f8PW(Kfj%miEyBh*-9en!*NzAb=^M$V=UlJR0f zn0uc`Hx)ux(lp_XyN8UZid<@?uT-IE_cG;#@%p3e9;Ik>jA2B}&LF@nhKFC#G&J^I z^{x>(JaO>&eD2)4o6Kbl67X(l{-}I zW{*bW;l9js=4P6>^0mZg{%X@)4k_lr={?#i3Nf~eCyYBX+Y5xF4?>&7BxHG(Z)C|q z!e{8;i1ZhHKt>cVV_Uk@4eu(XYm2#X7go)m1)4fJEyB?qgo*hBzp;m6cPrImKZZK zj5pC!{^8DZP0L|J3=R2Dr5Fqxje=`4yt#tl*hQ4xWIEv-+(H&An0p0vG9OP)40O z%8&PHnnGi{2LjS3p8kUMh=J&xV?fZ963!WyPoI>gB4>)-Mq@}@q_FZZve=~U1>Kg zKB0$t1xoN%w zq7}{-h$DU%TL}@N2I5IULW%^o5Ip-Vb#!O-qsF+~@ke^27Qg#F8q=CDH+(8h3&OPp zEfQxcW(@V5W&i2P|L7>eSM7s#n4X0dyALaxF-P(Z*R62mOE+pj-T$RdtQbCc0%2Fj zPjWx#!;#EC;h*&jkMPLCsFL3HXOI0NE`N71Y1lJ}0jrhDK8Cg(Z6+)4wJ|Gmw`M57Yenc?*Lx{sTZDR0%hK%ERpKi6ET;tGV5B2l0#ZZ@TYW85 zt`OR;1(Og%QK;y@OQH1@gO&beQ#d9VHa|d6*=B2_v%;e7^n5h4#}b_pMCZ}8MWyF) znu2MulT5$-7h#N$yDf!1TC*T?$xQ`;_-lnvz?2`nQ5()1E=BV?0kcs5K@22*O(I3~ zdhlvb(xZIkxxHhjMf2B2f{I_e&zNm0y>r_H?S@|H*c}T(AHYJ|@^;62KxA`N@P743 z^{;=rn0`vOF#YcPdTYDVpcch1l24g5Vj670Tn&pX##cJeL6U!F8~tHvUN4syp!EyK z6xjUK|AX}8ce-*39^$RV=a&EU! zcQxUO^!LRvefY8(Q5eNFUjQd~CZuicfRt3AjH0rABzJt??;v2E6r?!>7G7|!-t$PY z5L`3Ft8Pi3l)sziK%A42SQxj#hU~17(92&b9 zT#y_2Tz`QCqbCZ#)}HeWX2ezPNo+kTV?wwf2Vi_&h_|eiw%*)-q;GlA;EMGWMmV!d61^$9{CMgTk93|r z5xW>L&8L~~BRr4&-h&3kKP;O`h%5g^diiR9ZZsh>s;)36^H78|N9RGx)(^Y5z`_*4 z7q59Bh^Yi}-W#U(H3t5Zg!9l7u)`Slx(Al7ETg)!l_0{V9XFnkL9o3F90OpU8biKq z%n@#n^ILY5k$y8uDn@qjN4_w)K``IRvonRgV|z|b*QcgE;zL!&HtH#sr{#Zrd(9od z$`eQTu^-dkD3C?r52}|SR~b9XBe8degLG|ls3FG8t7FT0{2XvS1?2)WWdg~+#<*MK zGd$@Y0-8WeET`lrWj_so8{xG#+_p;8!H;z0fc3QohF$uwwTDV|CEs2uX}jnQ^}3oJ z#`?I(d;*I8#^Yk(C4B?+zvaILh~W1!_HS$+CljT1FC)OF+*Ky@9&jzgSBZF03Zcu3 za_)0cEVdB@re09I*t9D~)tYcu8L1Wv92jUSL3kYg)a#7&-@v><1FmN118~npytW&p zB#P>78u7v;Owbw?2UKW#TiSin$zt}4J5nyOPzoCik&j#J34n-o7D|GzHTAtJD*K#Y zgAZcs{3S^(m5gFD@Ovf5k!}5s^Ufu?IM3N|4&# zKH1If>kC?X7*#=?GV3Bo(D#wEaPgiAN%ScAnaXK;G5u|qq%Lys?TFrc&#>2@6tqw<^JEv)0fiaiEV>$vT-Q3p8 z`E9TEv=vJ2M@)G6z-oe>0Ak0FiHp*JO=cg-7cAQD@p1`fu6n%Fgr1dXHz<|C$5;nX zDcLY~|J=}zx{cOlw~5WHyp7&fK2`WDaP6J|N}CPJxF_8-<@QqjcVRSZNyN9ed&u_Y zj5qKZ%tS~%V`yU_LSB)%-gCekbxJ2>(t1xc6htE8+~z1Bfra<)0j(>yXu6PPNF}c| zHXuqSliLbrA}ERkmzW!N{89OQ~+;IVlg{geIrUk8s1_D3~9aBl7V=A*~;Qw{6AU-Z?z%jj^L zGZWtdT=;?OEEIqk_YZnQaJ_VgPE2ar3%ZdgvtMq~i}BLy+Z-a*sRQvQl`p2SQNG%S zQFe%s&U5tXc2io|TU!f&?c)UI!A(+?C?sCfC+|nhZRdTa0Fc)1mP5gk-h`k#vvd9d zb~mn5WFo@p_rY~w{OmOBTD~B5A!?{Qqn=dIfj88JYuKwb?}UTyA4WW*N|9`RU$V*N z5%XM0J;SOE5yrVxg@J?Reu@t4^rtFw5;l)Cah=eJKJMGknRVm`p|{3S8)4|@T_38d zQ4m=jq1J}O@Kx|JI#f5Ixz}c=&PP>^ocrssV%*ph|;=@ zKM-mHgp^-9*oTUr6;hP~@3Gb(BHPazzyoe{puOXtK@_L5mrtJ{RVDW}1gz|HCl;b+ zG$8(2jn zgK~31)W$XJQ{>q2cKs=e!m3myTzBx~Ra~8~_b5%R=!m9N&${1jrwZs8u!WO)!wI>w z$d4?rnW4+;k9CY7JC8U;n_Tday7JzHg?!`8m9rcgby84Y)WPfIS-Y%VW3Umq&G(cx zB-3%zBD z?WatxHHsH50+GYMr|jm++bD$A#F9}k&OpuY5_Tom1dAZ9oNqST+_`LpSnwoG+ztSF zLWRHP@?`4Y3EY(u=sj%mn>U9<8`P;a@ojA^woodA1<+pmvJFoz1d%)aUSG=iaP7wZ zPmIi|Ewu|>WaJ+m0LhE>mX!r^5l+1D$Wnx$nB&r*O8wqRPY^o(TY-DCNaf5r>r_bv z)PzEHjYsaoY_J3To@3mB9W;HuIp}$V6f%iu+s&FMF z&p?B;)nQ(mhl9Khr9thnti}4+2Rz6Ordf~~xWh_~$=?3R44xu4xE#4Bw9=6T3;t}T zgXye>BM%c2DYG6;43p$4rdcj4_@d*9KKWBPBX@s%cM+^(fPVlGH(7kiN?;hAb$@V- z#NlOY+oi?qVn3A>qBwK0cnXxQ$h@6_;cEaioY?jR+;9qnw5GIIB}Tv@yP9ash?`3&$NfSQ{el6}cqa7o$7y{hGx8 zxEEQ*-iJ`o)8uV#gX(ppeQHU-) zP+dXYQ#Usgq2G8!X?F38u^Qba z-YEO5~vnq)=4;2`RBAP!*f)4o(&1o zE-nWJpPea_iunqnY7F)~)5WrzCv_CMGMWc-N_WcwGJd@+#OTCJ?PTa6u&c#nOKHdM z#2JLa7(L~aM;z~SMx5r7UwysxpfDnV@}MDzd#*g(oPOsO&T`B$ zBOuo}g3G(IJk3Ka=*Hh;tK=8ggE1%oy;!S4Df8$?jnNn+h!FN}c5G|S#ta(E|Bhf; z*l1j!9arN_uF4aq|u4?Wo_mx&@elqnZe4mKu=dWW^wesO+#)dHg7yiFuq zF4FbfOe%5LOBcHZfG*EtjreK?Ici&l{7`nG`>q1 z90r+oS%FfCiDO(4J!P%@UVGjKk=jxeri!LoGoS>z`cH{`eKYousgC~C&YdV%j3&0T zC^qaNs=|qIllhU;W$?+%Z8pPjK3^zKWRoRmyCm5#MGRhKV$eJJoBM>Y$+@`*}lu(^}a_1~ARzr&Fl3 zO|<)2W-htm>ug@huXMk*&bTJoC9F59C!$0;C8X)dqWTV&LIrA!2667Kvny0Z3s;*72B+3|;`FsE z(S<{3DP;Eciq|v7D1?Z!)5lnsKidgsJ4_mk9W+kbo)*%k6P6e+PrWv!&DTyV0Ghc2 zVRzB^y_dR0d9ySPXPm!VjMs$YgPXDxjZx1#EpC_Pw?mUp)HFg5Q^Kb}dd`AOT9_HZBIip->v_ zB;bto=yWF602BHh4?h?!N_!^XfM&O|HG-X&wGxvQVT#MIFCFFVNQjo&>vNgo6Qp&* zmgxZSUrEQ;w?@&G?YfuH4(7o5IBu*2h^o8!#zh7UnUYbMk-#~m=F0Nn^&1ucJ#pKN zvo0_!CSBh#-;hyIJ-6jATjjzs$c6mg8IffnQS#&uu$jE${rne;kkRAyXMjSkLlm?2 zA?Z6=>SU7b&!RPY;8|k1i51;qrws}@yT*(r*)kk^9+K4hc0!gb+cYreM9eg8#SXVc zWeG+)zFBHI&t{jr{pnMiPSEXxFMH?Xx{u7WnQcHi+InncwT}By5?XZF^{K}tC&#Eq zcb1;$|1sq=Tp3=IWa$qfonR*-;V-Cc{mPw2CIpsSo3A_=lU7XNo-Xfi6v#89gb?@9 z(XKO@WR{RHR#5Y=ZdT7ZF0JraP&6bVgCAS=j}#5hl+VzFlk8_ZBe@@4F$xwMlLBhLAK2HVskN+ST9 z|H_r1-I&420AZkYyc?DFoRi3@U=?(~QT|>sPzaO&MqZv*Pn;z$+IA|rhg>u7>u=!X zorunZU}OgD0BLh4;lDKlD!vdwG{rUA(XAlO0=Tzmb#=;T!|@ys1>}9a56PQa$eS_{ zq2Z#$bEBz~1Fg3*LbfTbeHO5OSM{=rx0C_%1Qv@lDd?}1stZCxWkKE8`*$wQr6xgVf=n)Wm&t;(oiI|0i4aInIwt?QWHHtOs4SboG z(!o{8IzkACVF}@BpK-DIgTZ8jp3JWyYBrTRAnYF$iw137>}u%*PnnAME$*;I5+~@& zoU>0q_n=O{`{+_J(AhwJaJL=x*3aPP_G8(EFk`QNW>wsr&Fyd1L@q1&w{sLL zmQJ=gF8jayarSPHKz)kO-}rSf>YgQ-W|gY&gre9#eTGn?{IecuohDy*1T}%qtfm}uV^fj_oBT3J#daj0#)^4urH67+~utm z$S6vXp~<9H3`dPquhZ>}JsDrTnicbo5FlS9H!65>KO!Co7y3G<^6ZUnAS#p@RgRZq zY6hZHjk$^lYC5VU5iNYi=L|d%nssr;&Pe)aZ%@;6v*UYmHvM_jRaAiuJfZF=q~%_{ zu@+zI{%g|;<4qvsSNWbhHEK>~ugmLC(Iyfzhg{27U$%_-hfYD=3|=9oVB0_xh^A#| z!Dp&ct412{lMrO=F1xzRypz7I1KMOe!l1-z=->*VdSB)N6#O2P3rBP{^qqOu*jfZ>5g5!LjbtP9=zlg~N+qP`f zSZp2E7b$wnApMc#_!K|`!V!9Ming}Pe6Mf#h65m);Qq|Ui(ED8e!EKlc72FaimIy0 zt4+zFBd>}K%#jQZB0p*@$23Y5G7{el!n_;a4x-vZFK!hDpmYrILBvapOLMlRM!%%V zR(k`LKvx@A^FO6FXtcp6d4qV1`6L#{Gev48RAQFle$WfMdh-2SZCw0h&)0W)&G1pj z6=^i}n1>RO_1RyG9r&mXD|e86FOK-E7+lRVzIWF%zy0Z6>(bX;2nrPuAhY`{a4qPJ z$c5YM;ISqO26xsMS&Mu7in z$GgMzCd(8y;QQT&QW_JINJH0L-$Z>^fbp`$n;v9uLno5@*4M!k#p# z`>l6me6?$nY@^1!P3dHdABTIUk)>k$&?xmL*};L}K%H7k*z)%35ivxzWu^I(i6V;_ zgDjHu%M6#{^F6CDd-$q)`j`h{va(L9bMWMzAVHh4*78{cSMjfWSNx2I=F8Jfz%txR zY#5j0?$54Y!=}%`hVT-zp|s*3M%l*9rQp-$s;7A-PSV7|{E-ftk8FL*M)bApw>iNj_DB+e zL@;+0e0x@gqvY`#wSo{(F)xY&@+;JgsBRyiq{$dqekItIKK2%yQM3^Hj@`cysSV(B ziS4Q64_tT9N$m^~rf;M3Z&4psll!qA`*bT(SA<893~a`|xN8I<)6Z9}fXt$VXe~Fk zsfIQDCw$H5^sVi>VKneUa$y=JRvnb!m8sE2nQ_0b8#1;Bt(^Lph^w7gy?MVR*4g>Z`Y( zm-@xV;t+k(v9x;LuOr05VZB(>hL`vhX6spWZ+~lnT}pcnHVIFpG2~2<3Bxj=CydzR z?aGSQrOpo69Yd)RK)Bg#FmuX$eCsf`v(YKt`XJM@H~w3Tg@arjCAO2%OutguQWMi3 z${bO^`;eqpVa7IwxrsQYka^+2-iIn_s^tKkv-ged4FR>F&s4~O8+$0~_@H*UzGe>1 zb~G{d*HB40jj&}7J*LK=M^mZp2^vD@ne>sEu7YORr(6)ZSgSNU+dATBDOkL~>dZ$( zu8H+(^?Q6~oRQbu^&YiRlCC*PqcR$)DV{5OQ_^!ouC|Q3i8-s@IvRDfAal1DrKK@Jv&^s zy|$mOf9$s0UF%wIw(k@tUzt)}L}`v)>E0lA;t57AY#SEWDSkEEaK-J9R@NXfM~CA75Iu$|;uHMY5|S7FU^>*FOo{2gNdM1Fur{ zE>2vI0R?!>@P<(1i>uUg3c)91f_|5?8D;l91)T3*%jj?F0JAq+;*I;yNyw7=1KYc4 zLFlJLE+meXZ{74!`s9sTMD%~BC22{C4e3xS=jM=OA1mWXX2uLgJ{MORtFn+qa4>bw zyBlJ09NWDOslZ*<%@v+GUQoAsc~{F#LulxzENZ$ntzf}6Gv45_{Yw>)v)JpI@4M{l z5f}58J8Z=Ln9d&$q%9vov{%z~m$|lEwiWcf$cHWQ?!x1VI@tNJZinlYYiUCJ3hRTg+Re8u%S7k`!QMW z-r&)Zy_nM^bo3oO8VB@-hz~m>8K~$%eLtAFZqLFahH!rIsGiC@8{tP4w<>=u z`q#kR9V+_PJt_xxds;BGtO-|6on$Ub-AZm!7a6dkQ_aLmv>-4eCdD1YqPl8yC_qdd zDP)j?F;NNGTJBNtNWXzr7<*ZN<;+6VR7kMFI(%`#lfc7$SY>4^^duz9dalcsS(V$0 zGg=$@QDAje=E$+oxG1_Gab}PT!L~O+*A;*&BRcEX&2S{`kzs%E;>o~>mG`zC4XDg$ zSG8&UQu>NRRDZ7VO$$$IwE3setqgD70~oWW4zx9{>Q$vrjz|_xE;vs9?(q_yVoWtZ8Y1S!G?&ilR=3<1>&L>y;N_utEmOoIzgyh2%3f?1`y*TS6 zN;V>N7v-Rh@g19r)7XNKP8X3A-sb?umoD9P!J$x?aK$xHt}>uOkd|u*qINq|{mcI7 z!q*$8yVhs>oeAcS)w0=zN(L=7i#p?*FH%W%z&of(AKzOC3~55_R5<9a;ef}TXct7k zeJXfBuRjj_1i&nY5#OFJ@UpYFp`Z_53h{vKHi;vqtT37l^a2IHJortDF)V1rfkt#D zoDLXPSC}pdM;J!XdA{HQy?6>p9%lz5lk z4kHK`QRnz8k~{p^9%nPLDNLZi*yO~SZaX;=>xa6&lZQ-~^RJuV7%T;pbOSkY0FjfT z!^M>gdgD(ccWP_NVYVTf!UAE>9u(H`s>4$+vOAtom62By)glCRSt(%cds8Af2pjN; za^N*?ea-)F*lmo&lpp$>59@*-yU2aHE*s{z;GQu~*EBBvQd+i}_d5{7QlpRS{;OJS z|Ca@G$@TA#zP|`Ykf-Jx2-ZoW{KPshfgb?k<++-?B%A0i0XSzY0+FE;r!ZZdJ?N7U z?}YH_8&q`oVBt}Zn$ zeb$siU(DC%O&CJqHbq<;RkaGHO4Aaui7wSq0QKr`sT2#l5)E|kAx?g0xnLmhBE=nZ z_bq4S5{ofyGJApp_&JnH0yzw0xZhHgF_);8%c^{aqwQfiiq^{i#CNdbSPc3ZsZC>D zR6U6zUh$f?dG8Rbi-0{nyi_+V_(caWtH_FFN_@6BC&7Xt=Ri4j#IsN2n1@EbcROGB zrx)^l1l%pzSzg7|IV6ou=IC{#+|Cx8%7+cjB&YrjRHa${q!N zf!^}M<)v!|>e{o%$Osgs$UmRn&<2nA-+BtpbvyMO!MoLmy!*_0yPx9yt$QYpMWI&0 zIpAnEv>aXi7krERCSNNCs(G&_6vf()V90knZZer=K^ zrK(b~+jBDrL+OLb=WZUYUoZ726SRDt59{~UcpXHGrkihayfvOXYoPHTK3P=)RiYKa zzTZ96EmcPFsJpADNGvn)3$3G8{M4E;B)&1)E&1oDmEeog_XC9?c$h%wFQvRq!YTgR zZ71twdL5Qj&45C_9e;@kHssaPID~hofiE_Y7KNx%O@7g}1iJ4W@?uhROdc>d(@vE>fr{#nFnl6v3Utyj76?^Seg_c0;p^t({q}K&4DX5^Bs*5N)ZEF3>gl-9$%dGZb zmH@ecye(9=Y#n_Q*-pg)JxJRYkQ*-IC}q3>#b0Y=t!#n#(z3H0@X{!ZB~!7mUDu=P z)|0diedrAQ`TXKoUZ3?m{ip717{@uS zYbY=Kc3$Gq%AMoy*}4Sj$L)|&=U}C^><>N$Z)(G=*sg``P*H5JAZ@(QY_Bs>QZG?+->iL2Mi)eWe|OXQ^$@F~2y_`_AsuRwC|MZQRj%q@7V}DIb>CUv23271`bzWi(vlkozi01kb#pC)Tzj?U#R=6wV>_o`Dt z-A{E9p{tv;a5uVHS5aX)LaO-d35$~ZLCv8>B3U+xOq;=ux*QI1Hm~E6eyA42pM-^p z_GLV$y3I7H13X7&%fA~jFA>d?PToKh!D)L_+h(~~B)#VcTZkC2bonyV$Z(}av0ZzE zn^e)z{V?OAG(F@@*dJkCJG9POH}1!VkztI-TZvk7xyuPSobew^bl$2VzQAh*_+YRi z(x(yrKC)L~zI0Q9)(lT8t8cVeBT;i5Tt3N}yD+jsSIZg$Ej*|%E=ss~nENZq8#ioa zb%LB9ryijI10151_Y~z+>L!@y>IlYE8W-hMSkEQb@k?LoGp!GmY)|5j9h?TQccY5u zPE_F&p_IFc^i=4#FPSvf5sybhq@t>)X#oHZ0+p}SNgWFkj^H9!l$C}!y0^3Y3Mi;F zi8ekufC#H`_AzDOfccR}avJJNiI7mE9gV4GTU4b}6G6ZG^qq&&QWY0hEx6uH z!O?;u)1-JfZn?}_U9oi_1r5YofxVx{*~ap0qu+@A%8hW4 zsyO1yjF8T+HM+zihhq!BYW=XNJzBvv!^tcq&MljHs%75r=Y(Rs?cnTUhwuAyCxs}O zL^FjruEXeu&ITY* zavHEH=>#96khE@oK)e}uyKq74OxW)z6C)tI1i>F_%8PN33n(Sz^wxrEiISp~8?@6YT;wF{Z`*89HU|Hk7u=q~|HyE6={T^M^wBifJ8Lq0t(hT~Fbx z{bJT$B1G^M8oFEZ_?`#=M=3@aWD(;HD4sf1KD{`Nc#M zYXCDq%)hYom8Yt={ zMKVJXr>81}xS?S*J?5}6?&nXdPvxm{1z_}~wdd6|GfBWPNYw_C`-z1*RfA>B^>C`? zNmk-Y33&)oMg$NCMUc!-98>~rX|U_2=iZ_|8jN*>B1>~TFW%g(rmIfUMWm~z;Ldhv z_Nq0S1ghn!1PgwIJ`jJjwnbB@&zXdk*AuM@;T924}TQkpw$>gadCT(w*EXly1rR!B>oIn@B@ux@Z0) zohc49FVB-;2St$+`TUouUR(6q-Peml2;f^y@ITcBK8!W@9_d}f&3wP0!Jf9iO=zu` z*&l%Ne110jI3vs_(*H(NVd9XfZ~uvQ02(uS2UotY^Wg@?LDEB`Vw>K5xy}9RFs`74 z=>}?j-@Q+O7Qe~FV`0;xaRe7$WfL*3s9XPlF}PPNZY=t~>fm=7CrBKZ>VGp;#q;^g zfhD9Oa1S)&5ce3u3*OTQ_=l;drg?gDCv>1AY;EEf(B)PTeik@(xln#isEX+Cfo!Ukh1{^W^Pvnb zpBqYBhOMp!xMhBVfS!LYfna4~rW7Kuv@~?mo3>V_Qz; zzp#_PTQW`8Svg0!6k(0*zF{iv;!Bd0DrY4OJ;L(am4&1@7$Rzmgm&a}w!t-;=0oQL zg+|z!B(y503+d@p0IFgUDM-BW6OA<{fBLs^8C_`PuHh>krDr~=6w#Oog+bt9B?V4k zpJQ1V2wfFPTn927OSWe_G@>eMKJ{lQUPK$<1gUcuC;Po~jff}<?wm$4Wz*dz>|7K&sqi3SjdFQdi{%>cqG6A-85AEd$mF8 zsoap?m@_2en5c@oluC&-$Orazo5{kLz6ZMklfoEhx^`uZr~~KB?|D52(V({1&RO!U zXdj>Tebq0>aAdIC#@t;8bF`^ZWE+6=8&!7W$Wd#l9ZuBu(F^YkU`|aP%S&;`KxGW% zxqHP##(Xlu9=&l2PuS13GtB_q4Qo#KXr<5eNDnRBPH7z2egFQ<$44i!B#f-B?Ap%d zr9uZk-YMH{DUdL`k6q(RwI8wftBiOb*iCICDS+({&&PA_ATd7d7*;ML7x;5IP4 zB?yE6_NH~4UiN_mCR;8y_yI}4Rsl#ew^z8UhJRHNL|bzX?c<0pb2BOpXg9+j!O1bp zznP`P0aB?^_=2*P-%W*z+CtN`?VUv2%@489N+i)j0EsLYaQw&0axj}e2I!oHrp+4l z;!%G*F+GI1&dP`y5g!!;sY6bZG_*>cWYCA_zUS%b5W z1EqYYKV1|PtBQ3`6=UHm#9FOkr^>3$Miy79k9! zKi;=F6ljWqnse)w!2@*Jh04uNU_%g54p@A zln}=_DxCW5&Ln3eIBazz11%X?B9JSG!Vjc);VfQdU3x2CRL%*>^I z>GlTWjt)_bst&Nlk4k7WzdyNoH8B+<7i;#WUw`I|bcW9MfmneMi`D8F)6T(@GxPFW zcVdR}Z-adV$-sJHFOEvDEAqpprfnDAp3-+?+6NXPU!_2i<5Cn>IE#f=JRAOJ!qW|U zB2|6eOTcCcPC({W{?hxr{G94Z&w*Jo2Z?xpBT41iwER=~zC*;;fHdo-*o2sdu}CnX zwl#_hsp(}FN944?1~6p}IKM?ZuIWz=f%JW$^0=*q-(+VF1_EPL)$3Q%ATu`MB8_=ISN+-TeYWfMg~{w1F1l8=qRJ7HGHck;dowwXB_=A7!pM2Gsl`p^LK$?2Jwz4^KoR)(qRDFxPV1gE zX`F#eAq>uQa=zh#6}E(d`V#}}L{s$+oDY{+!~6h?Ei#Fd52 zVh@?h0N$hJI}FMb(!SaC>5Sis2Zne=MATg4e6rnwgYG6BSbUt0W1Tg{+!xP1t|+&w zbAs+i@FDmnDG?oX8c9%hgh>&(-{AdWr+;XxHKcwbfF&$He$J7U+ly;fEBjNW13I#J ze+5qGLE&uKkmQNOC1rVaej1}CSIG+o{U8#NXT=`Y3yVNJI&N{P>+9(EYt1omw&uR3 zY%-_ULGqbaHI-nUG#UWc+e+?{q8yJU-ydftL$%Ymx4g-~721&-+Ro|B{>-`up(kFcUEY%RXF^3VH-~lSwJUPvR zVZ^p5;|O(S-Pd0Fs2yk<$^v99C!fxSzB*u>d=@EOLt~RdDq6(B_Y_FY$|9N~Vr3Kz zM>{exWKy&k3VfI}(Xt_IWz6n(#KB6teGIyAVpsWAOu+3E~Dta4FEMJ6an6ukcn44v28<&-8jf zkv+a|rZRKAw9bA?$mTLtk0P9!r^M}=X)mLm6qzY-VHk1|j%;mGZd652Np!9L=kJL* zwLB{^urK5e=AyYHNDgNW{aE;IV=PI2tIF$eMl93(a^OH6PBgM+YI;Tf+&7OZAIRR- z`9P~a_|;)N1mE#?C#YO}XGZuQXmlYc7Hv^aA0&KFHvu2Rlu7jrArIeYgFGc580jGE zzD>#JLAGK9!m1fi&q<5$G$_JtT3wDy$LL3D%PRET3>J8~Xf--@WK()^>(OjS)hJ34 zypN1OykGQu%ZBTa*#7Waukon+Zatj`4=#j8q4T zvT!Gy99$_V4>^~^22#sIYA$E^+PaQ&D^O+!OrZIW{<q2=K`7NA=rm`LQU z73KCaoOSZg?iGgk@#3YJpY;)>8#D!w0@?&p}Ii4+dqmq-eKm_6w> zho8VVq4~r8IV(Q#IDvjIEgVI#ixf8z^mxPOn9uYe{M>1T0*<)ezyXT4oil;UpXW}pE_ z@gir=WWyLt_aSv{6h;KNtEit&dUrV|Fto#-!7c9{v&-UpH3m3Qw>@8u`3dy^-*jY&z3V6*U#FW<)!wV; zn^ANSM7PMeSknWk*$@s3u>eiCQral7u##g0$#n~uqC{tG`S+-BhWyavbfex^gDRHn9y$f8BWL}VZQ(O z2_IlOO5&i!iLQq#tOZ`2PKE~nUMxBNo)3lI8&TqaW}m{3DKUg?K+pAE(7<`f5DWPd z@HhY+n?C!Qmy8N}PQZkQ>ByGU0%5mxrfnA!JEq=gQ}x~N!mls6W2_`^@V{`~YLae3 zHyR$#82bIKH#K)swS-w?>Jic;?HY_t?S8RmiwhKV-sxRe|BH6W0VQ;ao8_}nT0`W? zp))~MIbjo7?h8b7Y4nHW0mK0K5Jz#zM#R?6OV7Y)Q%!8vf2!a&{Am0dfnDBLpu)Fu zz5Ze7B}cxIEB}xl0D)fizY1~l1ndM2?GpNDKR>#S*n0dO z4>Q0#z-30pKZ;cApM~n3rXb z2R~!)QTI$q)Q9^J?orjE;$~o`34U_e@u-K56ag%&i_if6GfzVC|)T^m(Se93fO_K*}#CSt$H2*MFbjrruNlfg41Phvd@tS`(kd^k$qgH z4<7H$a@#Yy$LB|~bbR^=ynF#XPzv>s6_Y$ds%v3jPsPd{So9m~e1({1v8Swt;Wg&0 zB9~<%-Hj&)fVLe>JdmIQ6!J)kMQ%2ty=u7>oU}2JF+ku6OPn(PL!Z*4d>NOq;s;Az z-E(mvqMnV*#6-~)s|69HPO#|l*ns{*rQMR|+yl?3FPQ24&mtOSsa}i3h^;?=>h$+q zsatA3ciUpeR|A5yKfM^OSQKxrIUt##O0*@#^BtOjQa$Zg19&ae)OyEFe$QCMPv7~N zd79K-AaNbFsAWW|RFY(-4(^~evXY$W=Bi7flR0Y+y*sBd&^>AFU~SMGh*ohMxyl1e zSF;^2h85Nt2D@a10b5809vGmANkb=B$H2kV8rCdI4M_7G-r@rq&x*Xq?bz^~S}AA) zGd}WtsYbhahd8Ic@2A3>_<^O0$>i>) z3tEG58Y!dcgW-ld1muaZhgAF0n|TTEo#aivVCS>IaHyR*74C`C9Zp``1y_;!NjMvU z4y<03;pKc>KMk;;auz%swK7x> zM8Ja|C?MDIvv9^y;5E?>kp&{>D+SvQOVQpr>S)C$J$q?OarPSVY&tG)xf495jt_U0 z|BOugLc*AlzMz#1n(SxK=Z@=IGw|*t#Bnp{O!H#}Hb0yGI_P{HN?v&` zz45b&%FUsE7}JG4XhAhRPUID5K<$8>BaABT2*tlz0_MH##qv8b9%Hc*q1op({!M^Snw=w;*}H9R0E@13mFuk?3V2*83T| zN+zAOqDU`!uVOFkx*3LFu&E+5GDX}4kn-NoORO(?#0bKyRXwc!qgP-D5-gBiaw8Y) z59>20#xb0`V?4Xt%o9mCSTF}GR6Y@xEetU#=SZDcYcaS~C)L4Vs~>?vqcxN@$pT?^ z!zSS_DJ4FY6;FCutt=ffMe94h{SdM!)Kri^b4qH0{M)x$^5sLf+$g^(c+@8 z+imuHQp&p2JTZWeL7PB}YCMPN8cQo*DU&M4Ug@}Bd0H-zQ1%Y`YKJd~$XXTy;=v`^ zhsuyPdCrrkjS{^1oxVBNn#MeQ)3)p%nPq9C|FyWk0pMf}0W_1p3SnIgKic20?vHWT zKm`M+9xGcn)3o$+FY;ME>$};+PC`^1lCsz1ug)~ZH*F~{0%%^a7N0dM3XY<4oj<(U zE!h44;q$urs5xoGZF4bP^v_l2=#qFsRu7RZ1kkDlzct^?YT77p>?J8&KdRvbs49YE zjx;@e?CrJBvi7tvC^iEsx@!py%fAQ&KkT1mYC*`zCvK zL`7zN_Si3BqozM#lH=b=vkBglkW_Q`5=NK;JB4R~;_Z9`chM=dn}B`r#8xN!YYRny zEAW%L`JcsMm8~FGRB1lP;xRM6@O%sdNSw->d~!lw+T1Bd0~CB0_941CtLy;5@4E1o zu`9K79ISKR1Jzb6ot#q-&MgRsl|1^23U~K?eq334T8WlN`}%IuY1=6&bYAZA>A3_0 z0_li29rBSCrr&ybjz7;g=j&k{68VX}1G)wVX<3K2{&<&idcqnQU~)7(av0!#lW_co z&q90!j}f7DGx%GWzv(#>RKOAW2>Pr9^P)<#Z;RR*O<~pxtp!_mJ9Cm|-4!tL)sD|H z3KiHG@2wl{`9J%bn^`VgQT7+B;KC?Lq)kWtRb@ z_}6cP0noll&@d9yR43GvLSB0>kYl`6s0|BnOflZu1WhYO=rU})AHU6>{9>`IK7%v2I^D2~v^kl%-@})&iYvra zffmoONu^bi2DQQGNN26f%f)XGcy%Yp*U90yfU8bc&D%fW+G&i=>S>w#wB|=z%LS)} z5HA3^4jXYFEQyv>xe9p5@&&ouk_Vy*W8$nP;e-aW$%^bpBnq^)*lI{5`{SR%9}-|c zJB}L9{3ceE^U-P|hE`5ezc(-#L;EY&CC7NRZ63DQ<+HnBK>d!6NUYldM*%KLqzX2g z&vmQ_O=Ecl{S3G775o2fQ<0@?5PZ;rb;GilN^c_~yZcxD}9YB^@3d6#97?SN!h9;5KXeN#MUyI{q8Q6LnC=~}0 z&c*|L{Ky+N1Z9ij=Dy1anA;5K53mLm(?0%Y;MNxyPzz1QIGrx<%1wk;{fnzbh23fw zciZJ=o5XTv%W)fb9YSb12UghXuj7O04}=4EnZ_Q~+L5y+XMZrcKP0XU7Ru;*$co7H zRpU(U%ydy^^`ujMN%_bJBbWi=Hgfncs-trl92SAqZdXCQTbL`9HLA!2xMss#nlFT%ig9PMkS@GuZL z*9#vNpYkzBH({=YJm?KGfyeP%r#@a#YIP- z7a%xp1}~C=w@2oR`^o~|NiM?7?85y2!ka0Af(~w??vI)UGKv0+Cnu)m6UNK7e&N9! zrhZ9P9TPLv-hQv1(`hdo@oL^Q^ulxcb)%${B&AHk#WT2kcm`k3ac({z2~L4#W^{{J zA4PU5=`*#A!B=kHwZB#V%9}ImqV55li|V1)r}*^sv?E|bPPJ05;Ku=LoSJnoZz%oytZNrR5LlE`-orb5@POqncTI?O zZ`<5Yox4D|J6zWhFbLaDE^OS5DV)usiHG5W&Y=Gs5b~u!G&D=)Hf?+ZqjU@@jZ%Ua ztJDO-sDscA9sqXTPVvvjE@sIB!V$^Fcmjc#`!w5VYzy$2DTb$}`M*dep4h%@pyh?@8H7Er5z~eiKXWvAt@9+R zqHkJW?yQ_6=G1kA*MBLh}} z#i{uWxjd1d9Lkidr!3&lO!|oUJ*=OQ7eI;z~iTeg^qGZ#WnJ6PztLi4p zl(%V?rCYckt0=9Ms?b*RzuW}^rlF&Np4Sww*25xR7*G7jXZE*ir7t7+>OPN=ywFKV z;&52GQIY7Y2}C_`4y$nJg9m^R>DVG-cdVkadn}v8`_C&VZ8uYT+v*0V7l?3at~JdC z4)t%|f4p>c`?X`l2>$0&>B(^z%7gXPzu2@~9Oy-Yc#YmTK}X;`>Gh>D8H4akU)R*h zjQ)4T3U^+jaxR8Q=e1J)H62ibRHW&Lv6@Ok=aY&Tpp58kP;y%DE$acY+<@ktAZWVg zq*~lDpFAlK7HmSN*JS83W3`Sjy1PCCWD&jsqt!3ty2508|5_&D0S15+OZ{emsMu1R zS_ekwAi7${G!lOJE2G-TFIL2*mvlw=zvAp;x{*UDg z62vK;G`RZ}+0eirIh3DKx)4;SPY1*iSqc^Q+iIpb%D*%`RAj;Y%;C9Ub@JexAt2~e z589b}nY6~3L?h_r8E#P%#Up!@)x^a|=;BuP9_L(heR7}0M4I=e1Y=t>12Rj9sJ z4Y6zy+CrkYSJ_Btaw})u)1($_sRD(3V}3n5Mh$Z-1IH(CLPl79Yc^FKM13TQp3vt= zzi)F*KnqcQy@cn`1E7DFmHih_VfQ85n9DVx;NVGWr-Lqgm1QjW;M>iO+CW{Mla-~k zi}p7c0(2;OWK{zacCCbb4s?!RX+)4i$eY+r7^#I_+Vl5xu)v?YVG~C=F$dy zz8@AthAm;T3sY31yR)C_X$Z@A@8i8=5km_`ki3aCnt|lf*8@$0r?x|$Pp&hDrih#e zvuz>S?f(Gah~1GuNWBuD+JLgxj8i_5hL1&4tq6{69{{KS6)cT6a7JkrbO6%vllop?kys$jk&gxq)5^>7zCD@~+=>qHKb z*VN=HacMsuo#sLgf@-YR#1X$tBE&-F^XIi3l$}Hz=(N^&=ulJ^y>eYttR?P>;$dod zC6%kko!V3jP3R`_JwmlYAcvdlPPVAu_K;bES0Jo<$MhlVmiAAtbF!-B!+9FttqnYSd=~>=J_ll&QV47Y84c* zg59ap81JBC$^(hLqij7j?zf)quc4dMWvf!l($AX$7+0xB)e+xyra0ii zeG;r7V_b~$@11f}EDfpDkKeb`d>&}*uymW{2ejHFrnOwgK(lrgHoeU(IVgQvL4=_& zcyH<0!GytK{6k)Zwu!M4^OQTP?Qtp|u&>muT2AQqljTf6`K-E@z z<{i}m!Z>xrCoC#l52QHAZiO?$g?=wPFsX){kC<^GM$TWgtT@5;Ta#N75@sc5ed;ix zxfJ;Jyj|_t0B#@Xyl2K)04v@@C4=gIsv8B z;UTNbio(KpKPcm$`D-HfHH-6U9w`oVLN(|u77Vf#I%i?Ny-%=p+yTL$lo$))R&gxA zXdKu9Km(Gs6|u+wT_WxB2f2eHoPQ(0;&4PS^P&!wPQF;d_a(PuRIai5${h~`{GuC& zQSDh**4+l;zS}izsq-%p-OJ}#=W7RO890btAIS)Eese8$H|&ODtAk&mr(cwvWU81m z3GZ?@?059B7mNuWEA?g(2U5x2%&^hFgo`}wV_#1jH2Evj z9xA%2)j~}n6Fk<%$89l*N|w@57*WawmUYp+d*#m#!7j12!k{%)w#S0KXB-yI)yLh; zGCHn>i7w+QRT|Mml)8Ev`X?j;?PTc+ccCOLIVcs)FUJxr&QhU=5yklGpZ4viO&)aY zXS)SOLlFov58p?zl9#&&%lcO2DPoW52jKG$x^GhS1JPo6oiniazB5n3{+qRXH9?rV z^^W3a3m&d%;TX%AMw%=jy=xvb4$ThIfnKi;HXBz(9UKr?>YvS01c(@pcu{6X)QulC zCYPzufpX&_(7r?;tlMDfZVrR@q6XT8#MmQSQ_4`V6laN>dfDqlHjUi0cOkL*`EC%L zT}@MD^qcn!HmjydqcRlUW{i7#x>lj`-Y!Sl#9TyN_A+_Vb¥nG_K&L4nj z=ZvLasA#||%3fVmT*K|rVD_VLCo$@0as0TxP8guBu#FuW@*ocqUc**j32oV5sx!B# z3okr1@>=lAN6y}*f~=wy_v84A@dF}3rhCa?5WxuqRj

    QLjb?Eb3N9f%5aOjl%kr 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 =bAhTe8!BM7<%j50O>}O5K{-`m_ zzm(Z?oQZhYw;N;MBlhwRnvVT0W9&yVdyY#UpMF9x_9rv@Q<=_~SLDz7-7M}@9h3NW z#UU-vgDWg~F5~%(k7s-$f3eNhcnxU^FYS?GCrCy?b-eNGG=*0|JCc) zmgmtswG5;`*`oEM{axI%_%1Q$t>0D|LqDm0Qt0p8TKS)+GLYZ{vJa;Irp1xpgqZW% z`4sPnn0tJ;R_?`Dp1!u2QO{v*(l3{OSj;|`^eO45#cd_&I3VU4g4@GkzeeTf9_1J+ z#(zK@{!baJ&a+;b;(hg9Rlod_#0}DCG<}olhcz8K{%oIh{$kfzpRRX^U7yVK1E!Pz zfYz7iKPYzpqhgPL+;sf82iE%(*6sT6gW z@!&%1Gq|ijvFCr-bmFzCJw*QaZx*}%39AjYkestpJyNnbF{{_{1YhPBXdZu6boz`^Z$2WRg)ShD8<}r%jBYs}8 z=bO4)#m`9mHF2N#xrzBE?iTUK5)X;H#2-q`^6nA)^6nHz{?sr1hp1obvsWDTiLNHF ztiwk#-em3fHOi0U&+nxCuND{j7vq{R{qJDnO^PFbnDcyR-t`mAdq}%94j%6zWn8{N z<&AOq@?B9X-$&G@NsPl&ULS|=65m+K<#iAp(w46)aGj!4<%#PQjMHz|@;pI#kUsN4 z?H|Q4j?4bDOZK!e68X2OypccS{;lH3k9Sp}pO?zVyP>ve`iq)J;yMQ$*E!JHpN7uz z^3C>GUhL7sp8U6Jd-VLbh$DaQRn+ZK%l9gBU4(CbPo@01Zo)Udm#>@rMa9v-qJB1L z`-}RaJlrc4<>6jO?p1raEe{;!>wR;jKfYSrqFjFuSn+3hmc(Iy$G=wXeSfm=74OjW zhZCdkVf~s;e>C(vrL#Wc9uoZ7KfBI#9@lA~t`DnzUFY62*AJ>bU7wV_>)hMq`kd@t z=a~`L>HmcOw<>Rk^iSLJb&5m(ne=hh*XNRs{Qymj``FC)cjmE~xk4^q@+=SVb7Bijn_cOT~-% zmur=K8qH@Em#5*TWvk1tS)SCT-JhLPeEiy??dr8!*Cb|)+_9!^M$gC_bBi-(tjjI5 zdDjKiiAVj6#%xil{O$isnrfL#KBKL5BC}XNpGtAg$fIZWj(V5I%RfqJF5`ua7c(wSXUm^)TgIIk_hj6cnDcYKqckajmFZ4XajC#q@E4Z<0Q*9e{QtWr9y< z+@yWH>s;$|Zp-ZH6S&@+aev12iQIm7#zPs8WjvYj!Hf@Qe9V~o;haC}10KzIU&i}0 zKA17j8GAggjX3vbOdrMd?HLbbJectgW9}QJO{l9=rdbU>Y|&KoQQM?5esED@ict`xnG z-=g;8@i&V-KF6nE><2UZVbh5}W{mw*W^j$Ey!_itXZ??hJGD+o+;`6Y{)&<`MkOBC^g!Zn z?%OxMPJBfC& z_>YRipYqMA{f9lwy=cMsPa5MtBMyJ`W9E;}7<}lIuTkgZQ9iiC_!+;b_xX#Tr!tYy z$8^r=_Pn1v82fgeV}?ED9T5+d-nu?3o-n37hs04H?2B{j?Kc=>-zg4z^4q3!(#Y=> zKi2y)l~=T%r;4|WqkW+>?RqWKCr!tG%9wb*zd&Ee^ak4=(VNAdAIB#zPoL@7Z#O2t zk<5NP(+_4k{SVLYXr{Bj2%Y>IRpr6t*Dm(*Z807Deq-#}U${N{i-?E4?=P@t|KRrY zXTu(y{<`ZN|6Jda>GUsLXH3rh=?{ngcR#GI`88>sk>FnaI@7-hzUW`7Z=v0Wu@d%M z^&2|emofcO*J*#ji~-MyU#342d{n=|#G4g|Jv?uGqsp^e>u-i1Bo9Ti7;V2*#z`!b#|{=DpuiGMpS zFU!O6(3j`9G4`i2`_rc5U+5qH-TJ#e#q}SVS4no|g97TpE>AI`R7x?m>`zGHu6}CFswT_MGLuL(CX{OB|3wc!HU3-vDbL+w zUW!(Y7u8C$y8LZUQhTadCTKD(dX2f|fBIdifd*zPt;|`uV-3$5qEZwF`x-Np+RFc{ z3~JSMge!m3yi_?=+%&IRc=@#HFXlxddVF3gerS@kN7Vl-smC*8c9eCw@^#Jo->c_C zFG+4W^FA}$SB!1lPxf8ni<3R?huf;@$eH)Y^^4;?^cL|>u@1#$LLbyr=){8~9`@s6 zx2K=%_HfwqKDi^}$LNm)&*|6sq%rYnvL1g)y2pnjKK31%eVf?r;jkzFE#k<(T>i|T z~{vV%p~%sU+w-qV$UBA|DRn_ zeJ44tgkFoH?QWO!C#QHfDL&&&*I0fN;>Zts`oUojH?Obf zt;Upho35R>|6XzUqw@_t*H3BV3Z3{}S~iJc+&kMB9N!`C&@!J!# ze>kA|7nf@F9&qfVJ>uUl+wk8lfB4nr->T{Ge{6&LfnpV@OgulzabDS!iRV!diyQSv zLNC-lT;D4Wo$|s_Ui@iKp;I0>%ER-z4QfB^V@NM})B3VK>he*UZWnXj821+Np6?yv z4N0F;yh82m&Q!kdiy7ybN;=Q$&WUw;U8eiLwR&&(6=vV35MlplJuf>U{)41be#T9s z{5*eK6JM2d>VxkrMSZY*a4aACfZA{9)DIl>Q`WcXJg>`l4C47)^kbP0hfaNPu!;J( zxA%zr;)|>Ih}@LQ&wE8`s+rd$ey#Lz@s7k_5>q#~B<8&#emvnlA-ko$P&Opq12QCj zTZQF&M~1~ONc<&Ts6MIoeqCbX&8j`b{u+Hr^%pukt3?dvJs;b&KI7W=fb>PRhs#nt zxJ~}Sl#l&Ol#lm}>=55-<=ZKa@=<=?`{Lyv&Ge~E=UQ?26QB3Qc>JTL^B$8^VvY$U z-eYoF++AV$9uw}hiRI%xCX3pi_l&gdy#WF zE}HpcAMtdnN!d2PmGaLSf7))?vpl2XSf15%&MRCWQH=1%-dUefD3524(tJ|RACi8Y zGA12fG(M{7n&y!w>HT8HftC{U{@KIghUEV~&7T&3uQFfNo_oRE{o+#gX4?_S~Bi_QanOM|{f5I92#_FL;OYiSu^2H{$_g?)|1e zyGwth{o+ae4wj^LES}c1>kFAqdkOuwWxqjXx>@r`#On}8Jh;agdya>0PkRdcvV7u* z504s`v7^4nufeq)(E?I+@4f7lp% z_8p!-+jiKax7zx^zsGcVt1N04YQd&QjH8;mi(*Cn!8?o@ z*Bll{d78xs#nGP8k7PRgBi9>kef_Og9_=NTmw3KEg11W#f9!W<_CuzV|F}5H2Olsd z-i+D9^TybpFnjW&Jw^Foj`_j(cZj2W@aByBG9EN0-X3wpBY%#!QGVi0m_0iCBiH9L z{kZAGKPC3~J}yoCHnk5gKl`KLR{1k7-Klj*f;sv|osi}nNeXG~iRo$G#+ z;#i(N;-lioAN^#ev%hh@%huO*vL6t~@)B>i*~7Gt@W-C^;r8A>$bVjXln?gyK|GHA z5f5&*^?`l6tq<~}eMEjR?IRd}Zyzx2!{gIFoV|SzkM$BXDG5H9@q+PFR2d7hYsXlL@d^4rF+Ra~ zQNQZn$BRyx-XMSUcjK&tKl_XDXS_ta+`93SCGoA5c|m;k{~mvnIQ$tuIw5{^vL`<4 zBlM3-r#{(dl8DFon#bEGcKe2g6?@9Zc+wLp_k!`Rz2Ybz@fXY<=3ev25Bp8xuqXcw z>YsZ4hondT=%bn5C3cuZ%gepR zQGWcF#O}}eWB8+wYP`$!z2eZ>e|0GDXn!2vYU0=*mHS_Dw0HE0OrOg1dDF39FecvV z%%1&M%JX?$@ z5Bo2-XZvycF=OnfGW&VciN9cseY3U~kI#Jy5g&bPrqh3ReK^zEAGv-w)8{h%r0KM$ zR&9TsTF0dFSqJ^bz5Z7|ySlFUAuR)GP`pdiU#zgWRXnYDf2(ysV*K=wIL1#uB%Szi z-`b3LQ2bZP{%^!X;&>LE{CIC`l{$ly~?q{FIE8mgqsgD_P)Ccw1)Kp)ed&NS3xcaqRDpoxi^P zuJzL_{${l|kKZHq`0W29{&)4AVERvQNd7N)aYOlR>W314RQXRxe`?|{i+8ELJuWRD z%ftAj=XXTx`7MYeKkDbW`X^C8-}o(!@2I^*dnMkG*y9b0J>G;k;(bTu;rd*s%1G*e zhptbkykHG-*%jx23vsYWu{4zDKX%j5HF%gZ90 zwus+g(>`C*?P1e={)Uw@tnukqoB!~->EW{UEaSt=YiA(KGM?rawp?v%yqxwP`8D@n zj#;c}T%K-@Whdr?nwOWW`TXX!wG?0T8toF zwYqMQ_^IL^aa+d`jmv;&aWO z{=X^axUcS1naCgZc-`WN_fFaK%xbII!)`xr_MegcNpVNA|0^-qNjWx<{!vUnB95#7 z5#FBp-xKo=f*$1?{Se;i&@0}U%Ex)YsQAhHBf$&$4StpE*_S;NQ3?NkvHPn@FWck4 zXpH}MwHNmv5W7Fe-N+CBLUk2P`KJ`m%TIf9{~5E#zgg=${I7kO#sS15W$E><7QItp zO8onYDc=dPm+z$5x`ONS2U2}JO&_Ri&~g7aw2UO`Yd{?J1s^h|ewV~i zzYDt0XNUR;5&y$i7e$-m{1d^Ht`yV9>e4zT@x0R>{l@<88XeGy^%PYrspp!iJ^q%~ z35j>P(jMbouFsWDd;D%H@6F-^;&`5m_=m+2|DsOy3DjQts&EC|n{mH!Tf4Ro@s-K` z3h|gY&Qq=ta~^+7(r*wS5pPWVCY6W%(M>7?=>y_Z;yAd8hHq)SgDgzp}P6Xnt39pG~|o;MetzlcSP8o&3>T6x((B`L1)`;Q9g6-!fKx zujhj)-nAoor;66c-z28rJ0OmJFa6kk;^@b|OFS*^PWJSJIiApmCD9K)B)+7=VzvA> zY2AB%{bJ9LaaYgpfY|d}5PN=%!`@SVo7G-CKlWF3`HAcD6W8S@uFFq+Px-OG^ZdHS zp5IQf=f`=D=XXf#`HhGpKh}TK+WPg|sP+@mMeP2C%I^N`zg*|M)%6~+ z>!&jPP^Rx(U++)*cYn@XJ^q1Ar@gvP|H=K2Wcpa9(;nTwQSAQQSLXWOOmA0vaD6e; z$1|PdtH*EB{?Yx9W_m5t*`K;U-*s{SnM~iI_UC$+*!_=X`uU#|0fpYs9jPn>za&v|dgQxB}y8FzR4TE-_b z?rW~MpU$}L{Ca&jgIs{hhh*@_=-| z57QZcIQ%L9yx7Z^>zmir>s)sVf8sf77`H}zOUyWF zjO&x%wE3@Q-`KcfPkgQmM|}L-q(}Z-2V#6Dt_yM9tI$7xrq3%^rP|>AZuCaqcNC7b$1_Y2!Cma=|;yXdnK)RcGvb)gHp0cam+;GRHf~a>ky% zP}pCu!eajyDZiJC2gHA6%=t|)-)rT%jDNq?nfRQad;CpWR*&BHpC=ye*W(R{J>D*H z#N!=uTh-p;9dgtU{okmcT<0Br$8>z!uQHKte&@QPO}}4Ln<4Rz&=LK-sja>s+^Yy;U6gx0K&b@ucMkyZwyWlON+buG2q_c<3iGeY@I6=+C%L z_q~WmQhq-of34r%#CJbK_YsT#-O4jA_WU>>i+J4kze#nsLorF*_s@OZU#+mb?|-ZK zTZy^vf2a5piNDV~R>jXxO#M!XBR zbDxU)x2QkjI{g>dd(=KKJO#d zIR29>EO`IVAx(cgG4KDG*7R!=ze4_tnto#9AAYDP7Bn5_e-HaWbsbw>j556?+5d9l zZ_EC;rsMo@BI!>``kzWaq3OpZKB)51w%(;SNus%2L%dyL zkH`66#6ur49cKG+`zdkQU#|So>C=!VF4H|rDr-~X&;3sIO@K=)xuCrB(!IQFe_met z2T@+?4|}ga#=pG&(7pa<#ZiAh`c>7Z`0M&3F&@V8EF;;yZCWQ|K{#$JggxV z0Q)(!->T(l)%JTd*|R3<}Uw)1czWg<_|3BZa=ZaKT z_Ms%s_hBC&+OuZmd>{R!*maK2pDw-i4LYt{O6QnL!k+VYx8Egpd-Sl!f4_X4Csf|x z&C2kna3%y6;KbbGI?iJp~hw_ltyo&XjMn&L6ydgJLfqy8G|b`GoV93(wBP zuRWwb{(iB?NB4MhE%m(k@Ut`VH*Kts-!Jy~=pKLMl6pS;sIxQi`S7vVPlwp!qkH^; zE9$vX8La{9-je{{mz5;D3{@O|W=j zYA^5}WBi9Rd$x_RC%+@&$nQce`;_>(`XjZ859;?hDLy*(u1{zBT&B;vuKZ4kBfoCN z8x!B4KT^(=f4lPbi^F zc;fQgd#bS8XJk9fPp9&cwB4?W_Y&+$+EM=9Q3`Lq6R zOU&||5c~3+6mPWhbg2HkJe$N(U&QA=T#rxx<+kL%>sqZ3@mnlE`Y)az{T0t|QtbKd z7e{{7@3c7T_Z!!!{Nn3U{Ht$MeTu)EnB)H`@twiX(!B~g9!7te@6dC+jPKB6&-i56 zQ{U5KuW$MeuTAk-UyEWd?~*vmOZ_mu>GjhnRx!);yUM>!{JMu$O#f?>xM@wrte?$d zuP?^iqrP~qOHF?HzBHKjzpHXDpTpW~%yWN};=!cD>_3C?KbhGxJ`p<4g>4YuZ2sKK z5IXVb?*_<%}KJ7K)6Yr4e@SHL6XrGZkO#2MRzDeyT@`u|p?#h_{UDy+EyEx)e zKH7Jb4<5>R+?f2hUK#PQ=X^W-iBEss5js2Tx|q{yTK?&!$OuWNlk9X8`;?ZC5c!jkG;(0VAZWF6Z)vucj$lrevMbFzI2LPrrV^mJzgst68e-lbo|l7ANvK3kGOrW#z)*9 z-Rm?%#P{J$E(N^PpCy+YhTV?mVKwGUq9sj5v>LJjZ$H!5ORb?D`h< zjh(yHc%0kR$98UNt>>PLSD5wLuJImUpL@l=e$XQx?QgiPzWu?W^R4N2t+!j%W=Jo2 zsE%j)oxeY%=TD^%>(}*3vFpb(o&JgIJ!1FYp6Ob}b^3IsYm=?h=}&n4ezC{jmFZKN zKAY)H+P>VMWFwibwJ{|uX)ww>nbdGj*N6$VxFrS(5yJ0GyQm` zbN$BsxnAe-xtTkf@=8d%d2L+Q(N!bJUs3-!!dXT4V~pL6jU zb6SUTn+^G#+J$F$Z8&4jddh5R?Yax|Ia-R$rXaE>EyctcbNH^5rM1QSd`?3{9=W%y zL-*QRH0E*2x6ZkZHlEC;nzOPhinR?zUS{Q$m06<**_^>M<`n0?F|YY^?^tEdO#M2Q z&CC<(*0SQPGAHx8Esy+m)pNbg+w}}u{hV{puzALrbI$O(;tZQB&af#fo$L2IG!^#) zeBjS2{z+OT=qzdI-0!hl?Du<&i{tt{{@b+mg#QWgX6+x}rUtY zx)f!iJlsDM<)J=$tUl1;&{^ISYQM3(lz&q3z5E?&mtKDESM>647f1Pf^`Oy$_}a98 z;(mw*)m7B@{Zx1DV%7nP^7M+MJmvmU{U^_Vli2g`7kmCZ=NkDxTKOLmKf&@}5PSX& zsw~gHL+trCsXylVH;X<0Zn5XzD~|kWZ;k4od3$RRNB*>@4so=nO_%6?5%F)P`e1$a zi=+L*M~$g3ILga6J=;}}Tguuj3cl&tq3Pbl+^@b#(_M+VU%ge+mn7!>=zW@gSz^B7 zxkuB_O1$mOx*uNCf1G$Yah$*KP0nM|9-VZ)33^1+X?V5#M(Bd^f0zFu@gM1rMEN&s z*}eRyHQ&o$NcZxqi&$6wcIi=m`j5NBasC1yG(LK5L(!n^U0u{Nv3#5#`||nuGRy1d zgDfxOH@>`_FURt-e0#;deDvph`8c2UbqGS z`IqfM?D=(wJwN)3o?n~T^Xn6Pe!IkxAKz4k??~mNemTF6`lddquc*)Oi@9GQbn2h+ z^r(OQIUfui=6u$f`_+T-=YFB^XZh)W#`5ETGTA>;W&BJk=W0JSwTquStvQSJeVaYA zYL>OtgEXu@ySR1LAZu2SUG%KFh-a~0ca|V+s|HznmI9{PT8*C05_|3HS*}|>cB4mJ zneJJc^r<$7m1&({ua?&ItTy+o^UV52TO((gZDGuM-BI&gF{!JnFE7_)U7gtMIGC*j zD5q{)HFo(Ltjux+?|R0%VnC=<{4(Xt{cU@syfW$BKR2)G*njX1^5kC%@xsSMtaHgxKwu#1U_Her0z&DuY>J}3@< zmTyt)`8R5OF7%%3b-r<_j#oMclAa?TZrAgUiNA22#?Q6>7S>eypmf&9*VX1oPm|7j zHr|-*(R(-4>u~75CB0GOckfF6G<58@8Drn4 z#^?5Z@^^dgc?f=^?1yE4N!p1~-a}$9?|=^G9&ey^#h?5~jBghg8ZZ2GRb2)5_b$r+ z6N$0kBR%rx{@^k3SCjrF@x1t3=07bCf9x9`QE%TXcKdd5*i)XCw)*lE;?OD2oY>2= zQ|#qAAolnp=1+OHW%ivL>+Ls)BR|&9h}i4%pg8iUJPXFyH>!QQ{h}JP+qXWlp0`_j z#(yGf?{i}JKW$9=9=Uv_{VtII+M@ViYVYWM9V_~mZ`FIm3f(`N+Rqo>qVMU7cdyZZ zN~3S6cqs9YWzX?Rm$}Pym-G=$eJwHZj*30rDY3_cJzlqbJ>FKa$K&wp@wk5C@nDa4 zQta`#e&_Kx|L}M_#U2m#c$_bIycw~_W4zeo(f{{&u*cge_INe1$D0*(c?{vJzj&3?;fv39Pwyx zJa6Uxo5kV(`9IY6&a}Ue^DUU?1DqKTb{;qW?qPl3QGAWsBnj@le?5;I^Zt~19glg& zhV)!rj6NcMsp^dMuwSa)>2$l=Ac_0qw(Iw`B~|Zp*r)0HB^~=iVz=jdX4vz++bQYs zz1tgJP`#VxU(}}|9hBasSU*ki-nCU@XPW+gB^NIgbN+v&>V$Mxx6W0?k4pSio%nG- z(~m2;xJB{zh<}pekBCRb|CadE;^X4A`XhZ)+@|eeqm`#a9OW76RC&eE*B@z7_D3|m zSYdJg3w5j(-;$W}s>@kdUe3>>yp+E|?f-1trFgrgFUp7XwyQK=A^vnKFaC@-hClDu zV0()7(In=4S;Z(5-z#1e-;w=EiM~a39eVjaWUWh&-!G2%EdN&d`|_VwdynO( zKAhj8_>4EUq~)W1>{9!W_K`E?T%jWj9HMi&fH@9az+wJ?rZqNM}VSk;jw_<--F-Yql zvZlQMBA$=rdMw9_xPC@`%!s2tcwY0Um~%c7eX^|%h~s%!>T9bw>WlTgM;!4eKkxsE z@)Q5CIO0z#^e(mEJ5qj>pY12g-z$5*Z`4|yuhgnO?pck@>wMSdHF?i!#L#a`?rECTo?0x?N~vo1Z>x+-wcdZr`e@me z@5Z`txf;IJ##^UOL~X>TUAIK0UQ_1a>IphDXG<+7&RD-@+H#`yAMm}fhY&40R*#(2 zv$Xc45@4-%QoIwj6;R@vCq4QN=9?#d;`rvtYWDjx`{{eJZ`9&?e42yD-)1`H*=bC9 z_Gb2nOvj#Mug71=?Ax^JL&v_$nE1V7FVB$a*pC}yzdy4-VLJAwjj?Z1^Y-}krMx@? z#@N%>bo(jOiGSD_`}xd%gEk|N-(ie>x7g$FF`f9F>xDn|`!f4urejav*6mMa_Vk@X z$9}Uh@q5Kyp1r1HKWvQssM$YH?YXAD!WC+RB<`Ib5_eWu^1h4@8sksjApB|Xj0Jmn z`;5tdo7l@cYC7fJXN>)%*;Bqov6qi`V>mZyM-%)Fby9{c-eZ4Mjm<~-ZW0e@S)Z?E zBCTdWr}=JwLVQp5TWxzH{-D_Hr%b0jv&PtSZs+!$wmo3a*ol{CQ0(?xiw(VuZ(PRD z>^p6HDB~NK(U3}Ba z_{L@Y%)V1?!|QXaaT#A+7vJb^8|E9*Ey# zjQyb4?GKuc{V`+g3$=H*@3ZXz`#r|kbG&i;lcr<8!L|?VyTxwr`+MxCGW+?=zFqCp z>xX+|gNeUO?D1zzCqC!OZr`N#8}{E+d*)}s(-=uTb z$dCMcjVbRoapaHvxannm7-N6f>?zNpJ~gD}7ym}-T@>&RZ6Cp(mH(8q7<=JeQ~RV(rTDyCim}Lew-oP+ zqW|abjv6xN-A|KZfA`ZNW5%WmZ4WUvMS0*T4}3ghuEmFrKV#|c4~NdZaf{-(H*VXT zwLepPjImeZ^X{;)hj|xRaQSX8wJ)#F9pb1@;`NCm9_87l^%>>i-Aps$csCQw`VPK2 z@e{PzBzWKfy4Nr<@eYbT-n{O<4u8sv9_3|udDl)XFFNlk4W0aXb|d^L|HQc~<%fGU zwi3(3yQccY@h&RLvs)bHAwC@O@jrduia+|P=6ZcV*R~@*@kYcRuS;Vy9Ju2o_ij$(w@1ubgwwpFaA9bt@l4F4u8titnqN?$&8yG zwxa)2S9R~Bs;o?xU9Nlkw63*^%JkNDHFj+uZHdu2-wFM;hgaVm`B>8LyiE6AYI{(Y zWx~Eq?LF+@@@U=5DUQLAQJq{dR=LOQcdGq|{nLI?_u`44n%Z9(PuoN2e@{HIhRn*; zrFdM6x+%pwtdlkRFTa#{_Ajbup%_;q5s!O=BOZMG0W18^ig!YMdu3j{;!zFdy}X}G zj6N#o(D7&NFyg)U5&EW#__GK|v^e77U!1q%-=XDe5Pv`Ahkjb^H{w%2JJepI zeyERTwb!T*@*fmO{$EypQ{wp)ucmlBJ@L52)K5+9_0zBR9Qpk<%O`$%vS|xf(LVqB(uSf>#+Rh!U9a+ti`68`MEOpM!yo(JwiSEo^Q1WP zL+^g%icY+m*yAmTBi;vOzu3NVFW3Lj_Sk%>j)ztD<-Pm#S7ZOm@d5VZ!?ZZ|AMg25 zL(!x5_9<=CBpS6Tx;?AY!b)vVo!e{ z>^YvDk{-u1bjBw_r#!riIm*Mc0KHklltLVfv6wi9TZ1A^iA47g-(2Qk3S{d;~x}9eClgS?Df^4{Jp+5 ziM_tM#Zh0Szm~`SXEXn$%%2NA;m`Khul-wWf7Is|an$Gb^VgR5j&`YzN$(JE)vs1b znb5as%JuDH*LP<6F4I3J|C;zQDgKgpNUSDRCiHPlxlY}LPX6@AJ^zE^$RGd1ng5Z@ ze=+kvX8y!Ko#~vO&z~ghg37z;gvBzgTC*rfe8dUp^{Z-CerE8b4{Ea^Lmz*E_{;Xdd z`?KGZJ|t$FCvmNudxwOobPk~bL1X$#sgZk%rPF2GydF*6!AH~YHCtiW#XH*$2E0vh2^^l;M)@OObTnb%M(*x#@XdG_<}PU%lCwXLo~ncf$^??~$bQ?uI$>GZRz(Nv+=~Ki{k! z()xWz(plbKt>0K)>T9pq>#JAmJL-%2-Ybs!=9{#<+oaGsAmI;(KRVCChK@h%{%urN z@-M$>D|Y>)>BNU4KK3)JtgvVOc3!wrUhF5tVbAh5>-;^Ix2z9w)CcvoRgLRaEQ}KC zmzQ70`epsY1Es2Wcv2p5*b{%d&gUaO?Q2#X?Thkl*7<$ZC*_+GNBOYl8{A>f^7XW? zEFarPgElVTK49NIn#8evP@cWwC{Ov#X|ev5sr=@&z_-sOarjf-0iE1aUR7iHTv&4b zfA6Ojt>Nd*qDB|`jC5q^j9D?G78CZ{_Mp@TZQk-^t+qV5JaNwstCeWkb(M)4!|?Yd z)r#7EF)xZ*&wX{!hJK$?t?fRUt4JO9)j|8l`;=<;uu@kIR9kiKJ>|et?_t_zj-|F)%SQ@FY|Z@ z8tUV16GuGqpAm=s55(LX{%Bhs`p<4ZE)IMAr!zij%Gc6Gse_6 z-}3SLZWnv`=s!n1>ie`f^6O|{SJ2u;<5hr ztFz$tT+efR#uLMy_IpD8DQ~|A#GzBZrVCff_uD#|9u^;6Q}rnryZSlPnwr}AnW^Vd zH>8<81GSB-+thh&tfST844aNi@1I+qVS~ZMSjUQ8W3j5uia|NmmhqX`T->|O1v7Ov zPk-ww$FU1~?z7FAh0&ZWuT^b+j+sL%uh?F;&g;3e=g2DSwD0L^V#}$o=kUGSJR6%U z)Nt$4Dsk?`=J`KIoagua*}di^n@jJtJXg!+j5fbYnd@xEF3{Wh>guQYjQTm#Ms=8N zUq6oxmARbPM|;8phQXQUqzx0*g}`d6gWce&Vf`ZnSJchWa$GuP=>naH29?a=QQ z>+oG@$Y)tscWYU1{-Cb&h}%r3FBJZlOK0qPqv_nc7y47AGq!w*>5byhZ-pY)UB%S=CE`hSzI%frRxrniZsd`F~j7sno(`lGKF z`gf&|i63SDqo)5v`m8vPefS?S{oD`fRw-=@zi9r2IO4ZU-z?r_dbjCMlFl=|SD3!b z^k+*yAbyPLyoWR5zg+rp@s*}8m|m0Ks=glE6p8Y;h{OLq(&;N+Rmr8^XZjyW9}z#+ z^kLKaPS;`a<4mV-8S(kf*J&}^IEnnZRvbFt4eL^0vb&N?y;B_eITPCc#ZNMQhv{6K z7?akeZHC0P!*Ttp`&K6ONlm%FU+nsHrXMn$Ya6p-zqTFTkP@Ke|r33 z^QV6DYk!y7*f&|8Daq{Yvz5Gkkqx|2~ z!Lv`>=X3N&`lPsDzZ=yCN!%MUq2DJ1O7Nn7gYn-cTle2CuJad%ed%wEKgVnL*Q`4K zDY5(09)j`TA$I?rV$YxBY1os0q525Mf0x+(cZ=Phv5c_CzgfqZ;CH@kZTVfmcThP> z#N&A7@utKc@3i?7Z>x?65fA@^V)vgG*ZG@$>96B~`yUd!|BTrEc~&ay@!xLD^6*@@ zFV88l$2%?dc#K7OJe~y!Cf+u&$LrE*rpN0Rdpy2l9{!Y%XYPZEHzxLYJO}Rac8fjU zj5z#>w?ShU!R7js?(q&v_jrtbc)SLUZG=DZcyDVk@p$i-$6FM8yd|;6>(N+8_!Ez7 zjlsm@-e-@uNn<4*kN50*Jg!xRKk;Ubi8mtlcsrWwihG?1|U&pcS2K!CR%pwP4;G3;!h9 zqjS7=9d@1me(1c182)bZCm!tf+&d8V#OL1OVD4>b)%NW7HnfRvwDRn1g*%x&0h_?4zR3;MTJ0T98cuf|sjIZr8@+;$u>*9;ue^}e6+rw@ThduYg@oa(L z3&(r*_Ep6z`TC#R>6&#FUS*#>kK5fg^r-uZXrcug`I@*JqoyANPmdpL?s_ zpBn+(pY6~6;qa$?8?=3dPW{1NA8^=HA3ST|_0gp5!Rv$XUU+?Q?bz#MNbLE+o*x|f zQ9ibhu&2JZYWwl!-6D?V<-OIr#qr*1?iCsnk87JC5pRz;;<3E2=MP8zyodUPw#Rr6 zHSroZ)W_pmw8w)z9vtz=f9HiO_77D2N%19Wqoki)rh7fbS0u)M$?PdF?D1ib->Sip zC=dSA8FO!D=){9P9vtz?{f!1of?5B3+b`BX@n@uaJlNyG;s18|kBOtb+^uDvRlH~) zlj1|-Xg}oDt9c*SABp!|_2~DtfYR;l)wyWIX54+o^*Sz+-DBh`09-<$aFq#u<2 z{lphZ@09;}nn${&SL5j7Ur0>(ro~>qLt-!AF|n7A^M{R9xMD~LmdOTPtJgk~^uTMX z@rx&>_*dUnl;6dR^+Wla)u!*P%**BB{37@p@`tZW`t)V$KZ)bJk9>!d^8sxVWy<-n zTHXceH|vk|q&HUMJ5Q{z7*YN_JN=i5Csh6g@e>lC(1CQjmZc*x^)n~-`k5C;{m@>P z#NJ*O#jj2NtltKXFNs;djpC?3>SIXx$MRGDZPLB`9G|@W+y@oqC;lO^$7g&b;-fPj z;yU9cp;JEt>c2*O^5>hG91@@M^S*1p5n?}ZeGrN5uUj13AN!A;;`^?11e;sPCzW%tEFV+{^BlntKR~4_IzP5fTM+eyyQ$fD3KQ!OvQ&@jmejQhiOz zjPcq#QheRYRKAPkQL-a_NX+^Ej>Om>F?)Ec@(TZ}WLFbE!s3sZ{%Xx{SIn;vT#5H; zQGeg7u)v@GrTgy|M|}JlFLnPmarm>m+-n-k%X_PM7CYX9^+0KJ;+HBP(nIu6729L2 zq@pPO#o>Rc^b_J}@4TmML}iHgl<}RUQMK1wwTvW~XIY((8uOmE7PUvt8A&kh+nH5w(YRsf?rr4xZ<&d?%Xx`kGhbV?QGf``<`> zI{EXSGQO+vNR^57ZT-ANh|yc!lRKQvXoL=4Yq; zUiA3wbdk*N;OGR9vY=6;(yNdexEhh(cba>-uM6i`~H95tL$su=kqzA z^KhN(T+cHXas3<-c6oerJ?+b@i{pEtE7LCX%Xje-^Y0W|<(KW*E3|5lN7OGabZO?F zBnO(<2PG$H`L3$cSJo8kOUy|_6Y3}5?MsZ`NoWwwRCB3SEcmN9w(rZdm(_z1b-7?yB&8lK%G%6YnC6&%aX7 z5;`D0tNBgZHz(NN5_-STr^P?H^j(x>AAbiSjZ5V3+=#I?lDd!3^7wl=wfr5NRH5ZQ zNiOnta7>|9{WpoK7Ns6JiRo7x}v-a{niWM6s6F+m(J!#LpI540(Hr2`#UuKTds>xEU#rm(}ru?4NBy ztN!aI>`9`C6ZDtA1G7`;KGgDef%XcWMlJheztE~b$N$w%l?%49sMcniTx?> zPpbT~Kjr>3u|MVUx9U&%J1uhmDW*Jok=NTILU*Q?_T33~*+`-ceoLa~1w%y{#Kmit@PUxh;V5xNQO9}4XeI)%F56y*Psx`0~MPxeQuIG#=H zkC8&F`pEwD3Z2*=^7vWx$3tQ~$o%T~>s_HOp;dk4_**En>MvQIJf2Q0Pky&bVtLzy zPAo4hv?@==li$6P7;nEAkBRY)3!ND6q|k}+(85<;&|`iRG^oI2VlDnsEq;N}iSffiPfDn-JYPwS zw@cU)>nqP666-6^FB0RO6nYZl4HEq!zn8d8LVT$csj{~4Ai?NM?3r|h%D!xd7CLpFQ)RIHy2aXjduJ}JkC&+KtaR@d8QyoF-^PK>u$=-T3+Tx9;8;&WoWl+tIkj3=+R zB*v5X+Y;k#7CJGWobMCkc@ngYC$FC*#yco`$ z65}-$Ix${B!u%`Szdb?Q?G-vPp1gjM7%x}o#CY<4oT`tEmn7y_rDeQr!k!o}B$FP@NJ#*_19Vmw3W#CW@fPK*~5Ix${Tv3;rhGTyudE$i1w=)`!{#e9$$PkvWb zV!T|T6XR`3&~m()VtY)?pCYs)ztE2S30mfFD7LS}{7r>U%wHgMV*c$3+FpM`C$=YB zoS!Ae%N05?-e#c_bkj{HJ9@+WASzkM}l{!T(C=9kZ2 zC+6Rkpk;gHcTXj@CtsW|C$^_R=)`#2giefCRUFJKZLdFJPmC89_QZIbg-(noAI4Pq z?e&+S?e!7e?U~>Sbcl&;-e77z!{$`<7e0e`LCi1VMzl^t7oUcl| zI88Hlh`Ig$h{gI(*24VH1vkac=|8lbXK3?0s&Ss7&m*#NKGis%xaaSCJ~cVd4jChM z!RNY&(C4#}ooncTDE~hcTDHs)+L9^%a#8nd|D_X+j?m|j;oSSWyt(ik;w8A8TWC|{ z_z#7ab#a6i`}%+5a&GNQDDXTo)J{6Tx}IZbIiLQALd$kKLZ8oenb1ikeB>!t3D&cc z4~WCqb6w7JlSw(pzLv#{(AK$DL-?I@XfZhcQ=!kQ^?5Y+oSJH#lNX%a=TW%m5P5QU zuFJWH7AIx@snGUnOsw^J4)ecl?0IwuVl`*Tt4UR%Y8DdIv{ak+?quzx?y{GPpMB2E zgh5?u7b?s*w3*KYG!Ijia{BIcI^x0m- zWe0m{Op4PGdnr_80fl0*KcYIt@6O1&%cUn^G=AG}e-f7;?4`nz6ubTnsT+~aNOew7 z$O1DmPuu?N1|yRlnW-wuY7`ctU*@GNa(fs?fa&eXfh3!2 zJXtWlm#op<{T>~>F{80@&6`bZkIMABt4~Um+tTV7r?A?Mtw(FcEMdD$+!K|HIE}TJ zQ!DJ|7GL8Qmrv})B3b?7_r<#~eWKm_Vmxr5W^%oR3LD!V@|}6PY$%j#QT(=RAvUSL zwi5TiIG|2*#mMthxv2f*6iEC2n*2C|x*;8(<4x+jnJ%wu$|aK<+B4M8a05Oq7HPjU zEdPpYrDdDitY!Z_^L;ZbS?@bZUs7pTPz*wG8`xfMB~wYm6R=CFz!BEIB@AEEp2-aj zrHlEM1MO68IJBjoI>#SQU4xX6c{8}7XD~g3^Uuznn29nnGwm(%cG^d<{cZ-liRrfl zutC)Gwz3C8GS^=g?FNQ_@ip%i55w?oN@d&sN!DYyV&2i$w@{mh3uy&vKcQ=EV9%8 zT!(@C79Z}cqT0XQHXFSdrwQvfOk6jU%cJbq^6Q{kjFFrMgI$c+i8Z_)9rhC3jOC2q z=+Tvf`-!x|!omH-=hAm^LKDNw-gU_RmE4kBGG9xsZ*kenUhcjGl>V}W=~p~y57|a+ zpL%wn$p8c{EHv#5{V`1#4Va{|U)axrMFP3^MpmSPb0OoLB_>Se|{EQFx zvOhjx`g=2w?>)fiMtd^*l`y(hu~%2|y=V1Xnw3*(uP{ZCf_6(~U&-Y*##zGkvH{6P zD{iNQUqeD2vB*z_g=u0bZ7kq=d-A|b;(u{JdFH53XO3!TSeasHApO`cr~S4Ra{;!y zzRTCy9bvB}aqM7Ite7{l(657vP=m~>tsBdn-?05}a6Nwbd^P9WY)GFQ@G>IeHlo$qoOO5m(P>_eK>C9_?5eoj7;nxFJ(QKaR1V$J`%+E zEq{~vpUo-0H)zRVV!1_4fU>j1(il5z&a%b24))ijm7U)5cpz~pWvr0%!SZ$MqINX7 zZ*VS^lL8A2*`xp_mMiM@U7lQf@P=UbdJ9dFTpUfcBWQNJDJhwwT{tM+N3}?#8NNYP zj7UO))L7*Fdt0kP_1{TbBX1QLp#+&zM~-vbqTEXNT^L_J?x) zI$LqJV)wJuv%$^oY=f4f8d2O2|HTg&&06pT8A$m2mmgi_#{6B3zs#p4s`iL({2%?l zTEAzn`TtOg>PJ(?SE3|Zr?gtA>Z%OqI{cLXUl40jE9ZZNl#TfxQPF>YB`@}CBg604 z3Sq*q!l94eU09fzS=iWrX3BY79!@dgQE4BMIU#dmJ^yod`bUGC2-hdXsj>g=HHG&U z4$aJ5KO%E|;n0bV{LP*l{9NM`!#*zD(W>F;oCS#z^Z}IdfyD)-%$2_Vh1VvUtg@XO;{67bPs!#Nuz< zxK3k~(zuiO*w~+zG0(C1k2{wF{}Jah&Ht@j-gu;R?ez~o^w2lokN1DzTn73dbS}00 zUA4tOUt9bqoy$D`@4p}E+b3c12c3({Ut3%J-L=KP(77b}leNWPMO*wS+Tu^Bg8lK| zTaT2?%xI~a9)FpRA;4<|IKch{tbo87Z)xO|A}QoVWAkwUB%#*!z?rNxlcws zBnPgv-E_~XGBp;vx|gr*FW(L3o`nCV`nOeh!@UkjsmwtKtNqt6BVg&*O)mbM&Me*g zip9U&xl~EmfDni=x)<*Q{f?dCZ7F@731Uy_hwtFxnk3Z2uFaXv^ltmudT>8KX`dp= zKYnqlR4n>6Dm$-)ZU4-SaQ5sl|BTW8)YbSGL0+|MHrQYIQeojn`FY}m%ttYSG|S9v z)^Kq{xuK=hspC)TS+=LWCA$Wtr4<#W``4_wyr`)AuA=n!isaJRpHbfJ_7r3Uhl|m- zp--@z-)PY0k=xd8UtYfI#cNz;1GBF`xuWz)S28YuEa+Ie)Pg!qadf$$Zn?S(dQ`Zm z)Plk?{!+CU$~9m7$Db%<@}OdKz*umooWGRe{Lf!n9PB3k=NC@_JO1;R7nh_H|MR=V zZ?`A@=Qj=Ke}2nw{^t)E&j0*cHA?v%l@Jy3J1QZn#H%G(JW#Mzt6zt}jC?|-u-^q=_nzy0yQ{gKcE|Mv`s zu4BY+0{D~6!T1CVzEulBWXK|@Au{UZbiG5sRZ*(s& z_HoW05&L{+MZ`YeSrM_%cT_~IO06P(=V}SK{0XbXu8?0_E#3XvYAJbqRBx5NJy^R( z#ZuzT{|0fsd!zXGa=TmGH28G*n82#)*9aL9eD_aYOOV@~NE7>ZoEs0^Y8)=y{$Y_R z6BCv{=1<(3zhZdTd)LV2#jXu58|$t4&kwu)*u~h`f9>wBm;ExM$B{*I&P&6L?oAJHHX?RlTPs4z1iZZE%;bs~mZ~>w|9#=exm7T@ZybDe zZeY@N84cZ$Wd|GnIiam5GNbh3wo4;}BlTV?+w9dX*GC4qn|=#k(VV2FsZ-7SJ!ul#@wN746;kYHUN2okhG&Q~%dV71=H$K>Y@9qe!3dvUrp3f^Xi^a!GgVv8!d8p%^*_ zt%xn+%sV{OZEx`%1LRne=rl9yiSimRxAv{EzvKK70Eri#jr%j*)U` zszpXFpe}*RuBh@Us(6Z}P)@)waL0c9)RxU#wQSyj#T&k?DHFy{nUL4HB~n_yqeHgP(QW~8h2^Lb2tA)dwKicNZti|3;Ms+0lFsj2U`A@HzR*V z+Hd_9+P*0#($+l*eIe~v+zgGYtm0jJt$ni&!LIs$svG62`|v5R!>*p=*rnBfKotJX zSzg(9q1E#^_dkUEyj^Bo(+hUq_A;Jb30tvU_ey+6%#-kMM1Nx`bUo@; zTKV%=AwO@Y8vpnenzw=4-1__T_k)%xks7h2VK z)0@yuS^kEvpxx9DJ_fDE^THdT>(ahU^RIFX>?yRD*$2((!szfSbQ6_dD?dLByISAJ zIzy}Nqj)D%i(lh<#538xm0Eo&Z$W;wJ^!S&_fD;T1#XWWNsXiGPb0qSpAPpx*JpWw zLGV}emTS0EFQ#41b8gy|Ui~%Vxfw60^~d$v{E~Sq+Ea%9!!>=Krmxp@mZq~cJzmo% zZgH0Pur}U~Yvb)L&HjNl{vX%+??5Wruja#_wDJGaQP@-2-p|&dK5D+}`x&$vAKtRg z{aKlJU|0Kt^;-W<)Yk7veSDTeyml=AnTe>6+TXpUjkh&3V6Q{})0%!s8*i(R!M_dt z|EbL%%|C)&&Hr;-OPtA|xwfO6``Q}c|-s?DeR_*WhXz}y3`aD_%{neT6KdPB zR^E@=_V(e$h|d!$UJ+#_Czo%{g-8KIc2jJg|?YU1IPw!~@iP~s? z8vWO5{dq`h&(L{@uZ}N9WI(IqnK_y+*9LYqU%Nuk4Om|17`6xX9M1#V{QLsP&rtdg z)5cq+XAn>AZ`*15@+z?N@j#>V4=7I^pL7}xyUF-P<iDi|CbU{Vv$Xbq(FpDB z%k~cq!e7m|cO8UQ zI#h;T%~w~~hej0foqA32NgaR4bVUME54)f3e-m(f6r(O zt+vlQH9g}R*wyjDtVPgje7&#r??^A~ChK=WH)u6p@740J-VVE(-+y=x?NP_y{f5G> z#_wdUy+3N>wPQ2*tMy-VKD3&zUbqZB(UtXkdOz%He(m%Lw3>gd-=Wp^yhrnYL)$)P zYU8tK1(c`8bH7uFr}ke9jzG6&edgZ7>}otL(#l`2t&eS5{yLu`zv}PZ zT75QZ^|@QCPpP`7kJ^8|pxM8F6#b=+|JVP3^3?iXp!LV6n*EV`;a{KqQ~o7rb$s!Z z*4~%3_0cmO{%ZRv)W*~I+WPlv<7KWJ?Za83_`Pb**XnrtU9JAx_n|FYI}Bl0IjY!eW2C9TmW`8U+x|QZL&W5wD$D<19mk(F6;rV=KBU0L96R=KJEJ1 zLz@4jweYXc^6sjI_H?KIQCr`wzd}ok$Dg)7f7IIdidNny2asPK4|eT<@+PyqD}F|O z)cic;FtnO)r)vGXOB;Vd%|85Fq}ImgVOI@Qd}nm#^R zD~_0yRz88>VGn$cGeo6}?I&Q5t*{V}TG_S384YPO z&bLH2+xAZz*!hirz;0HD{ny)9*i(_`O4gtG12L?hP_rVR%xK9kG?WY1ulHge(NY($ zXRcpwCEC^cCJnAnuZwo-fFk?Hy1=rF%erOMZ}x|N&i2PXK>dp`7_2zQPq;)&GG@v3~bm{(V5&gAZ{A zXa8B(A%5vGN1u`QXyhUZ@nR?X-}@TkKUwskeQPQf#EhWgn^*L(-Iu;TV2%6`p6`qw zVgA$q*zugSdoSmV!uaOvi2u@prVmPcT)wO%7qxzup&sdDo?Rf5BU=6d^FQsbVjl{I zx1xVk{uu3*AFQ`X<~Lt)+Kp!sf9@R%L z!`?6=w5$G!v_Svn&ij14%x~;-uAfjP*pEE+mc8qW9^n;ujxVE%WoO&|#K;T>1W zbn80U)%pu;$M`7!a=k;+9n17QO+%{e258dmuo4YZ7uDHQ`RNBqWw6pxk?Uqb7dbyHsl(e=u?FL>KG`hGu zZkP7xJt-2!+pl#s+CS7+%HBTRH)H>y){j2ELIW_q)cQ9MpnlD_cC^d)EUoQ~|E6W@ zN#B0`jr?HV=WJhu{d4RI>rH8QJ>ayPqtJhAyJP$D-tV*rX#c#~T6_L7);jGm+S^`w z@D3S2tn6xhevSH%pSR?3Y4<&Y{A&9!8e{ufbw&1YJO2;P@$Fq|$z)^rrahlZn%_Cc zmvM(BlZ`fQNBk=3d)HaN5bX!<=xUEo&s!{CjW2Ee#U6C7zrg2Mf0Zv9Wp5uIj(@d& zBGoKetkLeXdiMNiz2_|7>VW(w244A?^ov!ao#lttq5biDEqmg3{pcLu#zExI-aTNY z%pdXMj3sanwh!|Q*ekYcF-F>bFFVT*@DZ8AUq58qJ?%I@aQld`ezhv!9GCgMGo9_X z?nC^GCmcT^?e1rt@gonyUcc$JJ*C|>f@e^SA6SQaKKsbZXQe%`$T|M4a&|VO%I4Lx zrQP_GcC~%)M*NoDUY{$?p54y!W6La=Y|Q`eQM+RzKWX;ru=}42CQH9y3XjND`}0u0 zGW##JCsNN_<)mBj_G|ox@-Mk_=u0v=>~17cJil6BVEcJ#PRQOq%}aO&uj)Sm<3Ds` zzI`efXodXYd@yhqj=#OzEjw)SAC-SM;@5Q#Yhl|~+S9oBB3-Wkx`^L-{2lhGp7mK< zgrGf?h5bX?UGLk+FYzL$-8z8z@yOdZ4wM;vuQ~0;7}yv7^wS7w4{W1dZQs=q|G~@O zTrcgW`~pz9u>PSe)W6n)ua%O?fyXbBD7Kql!rry^!-Hh9djrN#;3c##ayPb*+Xg?h zNZO6oI3rc#Kd=|;=kZm)M5H;?oAW2@7ubq8TQX?80c`BSai?EVcda~>at zXix9GypGIo-s!X(ImrL!i=jek_q+&ufc<=BV{VBT zZJUetKZjBOuLh^u6_0=D%y0gP@l|=5GbxTlG|{drs3-CTTpXq83%ubqGWewjaZq0=6Uqx?^sCoPe7S5v3m z>q7q}rxlKo_P85%b^H-HjPkF&w*LWX5BH^g70T7mZzAG3WqYArKY#Y#hWR^iH}dQD z*aOb_-N;4#OI58VR$ z*!%x%Bkl3Nu&ee*czk+mpJn#=_Y9?-by}id?|K2WjWf;Tw1o=WW zVBhytn!W!Ck9XS5qp%Nc6Sj{ZqFGLR>>bMvwaHTANSDS9d`a*&i$M9eRr8`{MqQ**CmUOADBP#2m0^Z%HP=YL-24D=~g^{nI~Xx zaP7u-WU}vP*aIuke*OA}>rrj~=lZ$y%3E%b`2!hEC5n$R?@y7<(qYT$YkThuRq)->Hes#MDg*7^&{GEF0D9NCc8gz+G9L^*^=(Y z{&A1f9_Weq_q{pxl*}JH)QFhaM~l!qx^e1e`g=x`l7UR{Rh~OmsDu| zl*}LdshLFa`j4H$`hTOt@eMLLyp_i%Dt~E9CL8x({6#(c!%vslQ1hoX5&38PvkIia zeYmE>Zq~y1H7`p0MjAqz-TR(p`@7RVsU|FiS@3>{u;(FCjPrIdz3?>$NP5lI~tIN-SihoJcpz2@BlFG6lrc(`E|*mHf(y$h zSGc(1MU~X~Pb3}lSC<>!dqL)pu5@l+);vol8?Uvx;vva!hPHj)iShql=tFzO`c~P8 z3F0`tYPCzRYS_3*(`L(*+1H!p@?Y!~8P z{@C`{WIW$b_B3V$;-K=2+zP)crNs+wVu>DtJ$BgX7pelkdwy&2sq}Nd(m=upkt&{k zUDKS4>oV&4rt-4}WBzM)DGt=k;qZ$M08Jj>8EY_3PV}_w0z+jOk5usjyj~c}e`mX_ zm+D_dq@U+Ol})L&<-evcTFA4U@#h@(XuNJuvmCIk*64T)2yw9Cz z*FTri61;i#Zb%%MqaQ>6>|}d{hGJZ*`}SJ4VyWjhwj;k$4e|aizp2Xq1OAaWk>9-A zvdd7<9TXR+ebDm<-a`I>JMEnETdMruQNCA|e-Y#GxdyG+VyTyJvi!(sjFa=Nzs3Gn z$4%#(KgROSn$Ggo@keMM#%;#t@rz|6%slP*gU2b}`?3Ce6g**fM0l>#&*X7RWC+T+ zao^J&WIVNhRaAaf2K)w0Z)YRqMLp#H``5wF>_g?CE-;J2oP5&D6o zUx0p9b86ow?bgSzdp>je8QT%>#G=I)NWb6;*xipi{Q~s6a@CL|=@)qccH?!;?+4U# zL_LY1kv1oqhrOeO<2HcIg**2lhbJ>1Vu)cyDdQ z10&{k+TV8i1?X4jo{MfV#Nzp!_OG0NMiJr-UHRYv>1XYrT~V!H`gNSt_+jZ6rro`h z{%X9ipMSXPy_=+8;1<}!%bb1z-cMMO+Uk1gXB~z;NUHT_ypQ^2uIjN_`nl%A&qpf1 zz&7^FSN{!NWFm)Q2V;TI&8 zpXG&LcC`Z)q+jew_{B-(XC6kmzg?F+S^BxYwx>zMROI=YSp(bUC+{D&PoP61YDjk@ zavf;$I;=IhtDRo?%MSbH2iNu0Y#kj5DnIiwlso;M2ZPcf#xJ%H4R`v*envdcsJGTg zKWhl=0kU|1_%Qs6a<6<@`UN^Rk}%?As1EFbU$H*o@3wkU+I?@QN*KZIVBi+mLm#2N zjSn8Nx8Lw9&27KP>!6yitl5Y=sX_nCq(l5d><2u_plQM$xdFWQ^QS(PerB4z`V2Rz z{H%HKGrMi{NWW-T_{GQ&&-=}Ks9%@fSNtyhd{0~|VZ_2g5Qp58|?W$_ABhc z-!#8C{AQ*cFD1+M9)sP*xT?JomOJnJg_iUSvYkH0Rer|Li1&P@(W9iFbrSL7r1CTU z@EhH_-UHIl{WJWcWGDsw9Qzafa#P{`Go_z-z%6084uQ(gdlG&RY#ep1wEJqeln~by zK;;)HW7+A4FM6ZA{1EyMal-qY@j^WjujbC=fo>I!?* z()Hnf+_)nB)8cx4J9_F>Vkz)g;U+`!61&%uFXR==&9C+d@=@()BJ^MAk zE~sB&*NOK?Kch9u3#K~Djch}?S!wGANH;g@zWb7(8eGILS~B;l9Wi#GmMY?MCTuy^Ztt2o6sT z?>p3O;TKq9NjGEV%cblcpY?lPTf2V)J=Cf`Rx0w{llfaa=@?py>w4~GV3;~G3Vv26 zu)!C)f( z8m?peNcZc|<)raE7JFxK9{y#|!&kbh@e_E&vU5H?Vti13@c40j77T##1z_>`nS}OM zxG%}x!Mi`c+|KXa1zOa0;$Qs1S=QMGYYO82-Emfg$6KKR9!C&!Sx;N5e$efz{*hTI zC*$5%zn2+8!!cf5*Md<}1-HUg?T_IXK=nOU)+QYH=vw7dj0KR-w+OWOy;;WN$ggXa zPcb|g`J&k@pWm}(9Ao*KR{0b?4Ux~&oaNI$vXSNMn#YHc-_cL+Uz}s_SHlnB_%A|w zAB47){~Y+A>^)$LEIYb@_M5?&()73JU+%=Q*3v&V4}Ru+Fix%FTQ?$pxbe;7rN8eT z9PfMH0|V4*J$svDKeV};yP+NbSW^ij{u@ahxe0Ogdy{Ef#g+rIpgFQ?icUbatP4DTyo^i|MGMtuFa-`aq9-a?kMp7C#n zzxi339d@)?=|lM;IH8TL!?~bI9ihMXH2P;=VajmnXe~wgo;z3{Qq@0n4gB{G+i{ij zkN=h`VT4HQ7__wz^|3Ztw!KP)dUnNJ+`s!suiD?UKB14$pQG;GJw;~ptwB9p_kjU2 zatqsgE$Z|0`<>pF{^k()1xXk0uUZelKlYm?-HaJO)xK3Sun6a6?pr~V^e%votUVAsRg5{GcUySQ-p6468BT6bqrK^121*qq=lRa0RJKK7uN-+QUPcT&bMEOxdnoPYHD16EU*Y{wYC7gAAO?^oj{_AQF8 zv-u+XK-aUuKI}9s(oKdQV|obw#@mQrt6SbuSy1GT+ID%WKNLO7kZv)b_W<%|Psz2% zn`!)YzW6cIju+welt2#pwRH=ReQ0D}VNXMb`%049A|os>_961!QP6Lb3>fcG$Bygn z33_{hq28d%SJLyM>b*<#e5va$Y$wI*KaTc&IjpR`qC+0+pF)Gk!Ch)t&WCerS7$@!tV2CTb7Z(Yjx5w(|{!zcrNksKaFB5tO5!_l0=g=O*>@ zz{q;!)6egsJiqgj`uU!<4*B%+N2@_gyWUZrN9yO3)?>)0pJ#`7p6&Vt=g<0ix3z)o zE41wR>O4Ne^LY0ugn8Y=BK7Ma5v~XQI!J`qK|G{>UBqmNe$kId^y`rB=bPL8ZM_5< zd$=BkV|*69-+YmD@SL(QGZ<0D)6iDwCU*WXsq&l6ah!U1Rn)$Y;rWC4$>0g-*h$dy zCuj!~f6kg8Z^rzn;;VU4F|y)nJ8SVgc?|8a?zimp-mTlrkRL+)A;dW8A;aS^A6aB< zKk~zSWa`2!DFGMjP9$D2NojE;a`a3mO`wyuMAlc5tR zKh)UXwHVtPue0ZCSG{U7j1j9(HUfQ3$cCWGZ;nR(+*gmkW#_YC_mEas=tvhZO0DvT zyvW~l>dp3b85jKmo#F561jerh4W_I7<{0Gv;7IHVS-wTTa7XxuI)EnWX1b-;UmWA` z&bx22uXp+mU>*+s2zsdlhoA%0#rel0|CRd>s8Fs$xih}ty}IR=q&t?bMn%0~G& zc7D~~e|p}mZs(7@0lKMu|Ah8ZiyGQ~`gPm5x^7zs^LT(-zn<$o&GB~%>+jPhzuFsE zI2rlk<;V-b80o74t;#oisQ;*n3+&_f=pr2NdlrMC+sGwg_;%22j`~|)BLAUfzu6On zH}10A6(&PJL&vB+WR$d>|Kd-ebbUK@H5n$ilQHr*>Ed&Q4M|HqSIByX_E>hjxlG-&n_HASlxs%*Y?j;YBC&{CvBWQwX zcp=V*(?~Blk6cY|CZps*(x{012_YRHx5txh9VK^>F>*f{Cy$fmk076+=!kkoWFLcH z9+^+hBNviO$(7_faxHl#qVv=4KM;Q}d5}Cxo+Oh_z^@`%gRDwAqn=UmIu&ejoHXh} zmnU6hbO4cA#$fjf`GL?*YEjw;x433wt-uBcanGh?F;|XsCFmNI0stB57L{Y`@ zAkLkSrTik}#Q(fh!tjy7Q_!x{p!*~kQDmIhAjHWVaxh!wcl{2(;Bhca?fwJWa}11; z)<8>UGyd+mlvo8GckwxuIJLSCQ5@%Qo?G$m#d&$)T`)$i$N9VGT8vkjKg@X4dYr#| z?!|Qo&%wm_988c}#o6^M_AfDVKN%;Flg4lGD^FG@U8LQ~fAMGJG33i8^T>R19=VWQ zO0FeWl7A7}PRbU)^(U7+GM}7BE+m(dE6H`_TG9^k7k{FS5PugLBlnYW@;GTUhQEs} zPyR({JIPCXF6ko+$N;&R43Z&oHCa;pt^7gsZDbL-liW@2B@dD($)jXR@mE&I@!?u> z9l43zOl~E&kvqvEvZVNV%@IGJoJTGsmy#>Vwd5vp9a&QR8m)MIOEx7_$xdV%nNE%* zGsu$S7qvnBo#bwEFL{tWN}ePuwne@qvZVMmIy0d(;23eI1jJE6|LX*1NEL;Aiv5H~9z`7A z8qiz@8cV1d-_5w~RsFU5)GD6N*k_0ndYAED1p_aEv9;(Ii*fs?`ZIpk!ue z!vkBPsJC%E5c&-CzRBZ+ZD4Q@7>gOu9nQLDKUOboGb9 zU1VT}WydiMwC~Q9<_?))%}ce@V>g4|1z>0)7@ouQTR;^j*1wybui+POd@nzE?t(qK z9E?*3S3vvj1dWxTiW3@uIA4w(xkSdXmcSld3Wje7J$Hca#h|qeRB^lm5$A;&+aHs0 z%-OJq3z(1eu|2MtNDor0IHm`2*7RQHmvLg(z|YqojHff+0MP0OdZ<;L7~^dC{D-$? zoPY;@=3p>12#ittnBN$JbQLEw2=!@@bIQK%>du5cIu!Jf!C}xQ^M|Qb9PePnsZ}dy zqpXiH0{)@vn2+?1gpR8G)GCfS1aa2gu-Kjequ0aV=LO@VL3b7yVtx;`iW6g;QEz3~ z&nN( zB;)uVgkL-ay2$WD(7yXY^I=ftkxOhYt}ittw~+A?xNr{qSCe`^L_PC2ya4`rH-RTf zlj%w;KZ|zduc-XaM8>D|+Yxum5^x>0K`O2MGMGM6qw>?^7cE2F?4_X65lyS~=pFEj zYgBeUe$(af-yHaDxV%dyasXdSAp60g1JmrTKVl|dUg6KDnC8` z)^a%iN-7VwCy$Z^$w=Qy?jq+^K>ATl>+yHZ!2NVTulZ@J^t~_aKF)29c)VWA+yN@e%mTd&2&pW*R9CsyCbZh*#O741NyX? zFDqugi+RUe1V*R>e6PfM59!uBV2tUZw=KI}v7UH-^2lvZy(JT3$+hfs_bT3BEDIf= zj*`|ru!qU`&(JEa{=HG*4!mE+@5fT#lVx3vbRX&M1nuGXOsVgiQgOY{<9Vl@f6g2z zD;n*=>py8=jP&+|4w8Xh(7xVaoK$fmV-dG|O+4rw=>oq{S1?L?Ea(8~>jv%Z4#r3o zSO5O6SReRB_&s3i`@nqtkRBp~*FXo-L8CuuuN}i^dUZ4VlVV;M=%P+wI<=y*AM6VI z!EWHl9$;!uF!a7<#|^xP7qZ{(o<6~TquND=NEJW!5$fMB z`Hq7!etbCM8`pv^(j=ob5I;s8C*3UHB2|2AAL6^*pU;%>Lu_A|jF3^%RS)rltVf89 zalOY>x*OY<^$X^S-tDUYQ^pTIitRr12pH#aacCoSfZAFQ?P0q0813u8?c++xa7K(g zPP)b;y&<_h7g{m3gmh)kqkSQ19zi?2N!SlgpYc4-_fF$DDoTcbhxVOfdR<&EHjYEP zc)dH?5b5!wNDuHjyt;00{(`u0o*Bcyu<$3s5oBjcoZCeov% zXBIW7$~)hA#LeyG@8**T{fg(6-_gEvomT=)xjmla{NmH*8GDfY#h-b5@H|U22Ckzn zBA0%P^z^-8#qYo(a_K&3mH+RU{yqF-U9mmr^M*GC?JQ~j@NwQa*LlR?cB#bH z!?1ld@Aiy+LD4gb+XZRmL5In}WN2dw=p$pKcPi3jq}NAHdbs_n^-#VBj&Dq|J?SBH z$pE>U43kCVUb1RU#5-4>WIkgd*2mxNhYjvu)&AI|c9Z)4*~Rpd_R|sCU}45JzQM}w|02!Eb2MLlBlkS>vokM!|UuuNmTVxEWUoesH&Y*{65zjXmuSu z#`>w}9802l?@PCmls(LB9KSFwlthy80LoOwYNcYFEuOzpVojyhS7Sgi|dN*0)Gw5_OOji9I z=_5&1FU3uNz`m6%B3&ntog3WS=wMmUxB&E#G15~G z=@C-ZOEFlU`IDKSjFFxS%ul*6gbtCeiqLxfDqe*2dE`NIWF@2*k)1Av-bAW;DWd5|1=3DS#5y?)g%g}ugQ;8OA=nO7a@yUFy+p;f&Ut6quph2&8( zy9UyCl4&)eHLF@I>^-B6B)q;OMxtGi^k-m*o^-^3}8}_6+%untmGwL!wnVJH< zj;vY_+S>r!O%87eJ+cv4M0RQny@{;R1bQi{>ZRDeIqVC`C|RQg(sRkpq~S(-I=PzM zOS)UaK9Af^R&9lJFS&_4PNubnUDZqRIMdToVGokK$%bu^o=--|ifxhZ>hiBSoPO)b zIN7Nk@+~HJlB!;cp7!utOYSFAJ0Lwk7LnCEB0ZZ7D}6Q6!{kY_PbZ|WBx7XL&PXpH zw~?w|io-4Vb?V0WeL%B2Sk&98!@Z!BdVu@Ml%CLv`P9mPCF7`gVV0w7RWHSL{jpzG zY(E%!>i}^7Ag2y{peqgpQ>Yg+pW;ZSD}RG=R6JFVu2sDhcMm~1iupsSGeKSNX8QgN z*jH0;W+Eu)gsOqKY#n+q|A+pa(oNw-54(eK^$LL@2F8I0WuNbCQ{xw!O zmgKjA{=-S774(^-f~C}lmcB$kCdkWar*rZ8V3C@07I&rGSI5O zbS9-|!v?^r#CuS|0Qy(_RUTF=0e9v?*wYnJCT}H_0;dDc`M_* zBSfnEYw?oqv(Zn#-xe$3zFU;(>i%1l_u(Rp6C>4qIoWq|IbO4h&5Bo@8}sRRt~^M8 z#qG6Fj^bKPt8|m;%C2jbzU9eMcKwr{0zKqfazB|GhCM(Qk=36@dNvs*Pm+C}fqgZ( zmrQvU>6`zH9Lap`?`~q(Km8oJnSSTWrmGNNvHU&Iio0o7TG7jNW!JSzclW{bHeqsS zI`m#mFYbqQW&d|^7vs9F;d4Kv9)Go#p22#0`(r-Q@4rT`#P;eULswAqzNN1^^jY_5 zneOF%+Boy8`?fLK|L*>+o8`D!uIF-;7a@!9@2dLaYl@8adrwr71In6>c_~6#1=OT9 zhnlqJLWfE72IwH^ng<;ty*E;mp83!b((+T2R)Ctc7C@&oz<_FL{!5F`m5<|HYpzJMrG6@$tFwDE-cru1^tHF+i4-1i!z_0>vfdZhYXPI-$1(Jwi43!GChg@ zDOx_got}euuQdA{Swz2cW!fRcSDZ(!xRW}37=Gu<_Vo8?`SkW}J%YH~$RctlxtrWe z9wd*FC&{Fr5vL+qm8?NFBvZ*WavNEsF+I<;<3r~hVB+4c5q+m1Na+vmoS@4)X|8G0A~is?nrirMc$ z=aIWUf^v~7u z>FqPT7u!kgv%o&*$T0oRl?}%rzC~t``Q+|g*dtS%TuS?y3AM5FzkM|{4lAHa|`xk(FHLcQ1V)cMCe@T8SeG~mRt9*(q zzbaXS%q3O+zvE{5m$U+K7F&|_e||dd693O{$A7T}rPT9C>iMML=&E+8SQeAL{+$_;P6V-m!X)$~cbxIbTNj zeIz>7b5kn)eD%L8UkTk~sOPcN^I7Jr?3ZnzdQM9{$7TMG{yAT&?^)5Qp4(FC=d1r+ z`AX^^{rkz)`}Khi=pXgoy?PJd`UCxQzEs}_pi_Mhx=KG^{cp?nKDPhRx455S_{Z4y zGd%phD|c<2KZSn6d93Fo=;QZ7#b}TGiFET6=w*KIY3Q(lI7VsEXHu61t@30U+L=E_ z9q0FL#eYM*0KdP=m&7<1fI<2Nsm*ds=l5X+*$#t@9%OxYV2|qk1pT4E7vZVtwue%% zHW(mXq>qd%@;wDZ*;W3lkl!NJdlqIbrjsEuN zdw;{N-XGz695Gc6`$d1BBh2T(y^T?xhx9grj*&j5tM@V1K~lc@IoNtETN@e=t0+s}#N_m7kVB{U27 zzg)Ay02wEJ1xSyPo;lD_(wYk$Ce0h5gQPJJ+DFDn&y7frknZ`=A=2%qCS3vO02wDE z3y|)<2@I3w&Co&8wUC-LZh=<);{6K!vSWVTH)OwrhcezUayWS{IfA^797&EMuO~;7 zUNVauLyjf0$sBSVIiAcVCy*1#Nn{>5nVdpSC4J;HFghJ{<%8~-B*()mx8gmYGO{!N zVyUeCo`~);WeM^p=?3cQe`C*Gf&6$|{Oll?{!RH;twH{h&O5LCoBTgLj{Kap&#HiR z3iEGd8^+C*A1@m!oz8jOL{sgU;s2kGpAg%(_HO5TynHh>u3j0&`rDz^I7!j$?}T9I zTVzHH&E8SdJ2d|St^9{IT{ejLWx0kIYW6}c-i1%XUyX~-_d=_2Hm3+0S9J_y?HACM z89z<)FVxx}ZsV~%|I_Eq)m#3*Q_i0^q5c2q_Ne%3zADT9R_n#SX(`gwdU4UN)(h_1 ziSG|*I*zUlqZ;+4B(A;~2Hs*b4E(B-Vc=T`4Flh1Xc+1(blf#I4BS;R417y~c>kI4 zYLj)yx?~DjkE~BNARCg6$i`$75>Ejc2HsXT40WBN1?@QcF!=2WhS7@YcuL$bFgqCr zuHLGrw+y2l{o0cq$d2UIWGAvSsn$_O=z{A>VFQcw!@F#fmYk;gcxpS|{VL+A@pe-w*wy^dw+ghH9~x-wzx*25 z)qK#jDYTk5QeH=V)jzAX_&;dn@Ba|~YCgMGtA9l6zbvhO6E*)}ChDW+sbC#wHNSQI z8Re<@ae$_KYyDqIJC0kdt$*)75l^k}((gL;uzx_S{@$nQ+?!!n^UA2lpw<3)fp#2M zeh2KTzoM@}tN#A^aoa+Lp~mxa9xq(L`DVvrQ3 z_ci}dcEVq6AK6;_pL_;(H6KpX?91ec$^GtK`kt-P1C^44hW@4p-6tNqywE&i{!!(Nu-%h1MCIZdzO`GnejpV#_pjW(Ve zRddcyS7`n5i01!}*1qN1cT{aTkU_F^@3gPUps34|GRymztsNfe7CQE_x2Rl#{2(i+gBxRd;NEBZ-cb`M-y#( zcF%sQ;`03RrMrFeTDg6nRNv>z%Fj*qjmw!ZE@yi4?3`IGCTIDwD@>l6eO6*}Lt)Oz zYm{6eJI9wiHp@4~H+^dU)aFyCk8d%4Y70A|#jMuJvsyP7UtZBHE6NVZ++@>M?Cr zs;5VbYx)fy({IRx$-e$))$~lr8IxtmcIejZvF%gGr)Q-OX)(7)k7>T{1!LMy9G)?0 zK%49yecKJard>{UevAAO8NGV9ahDv@9u_mkW=)ZSTOsiD{_bABu^AIH`_J-Z4w%%c zd##hjc<`NefretGi1OH2-vG<_w<66 zbBDBQnL2g0Z{nPG(`IzfnNTn$bMT~@xr4gRo_0aMb44Qn!BO2+KjxxI!>=r?4<^wgd`(tXoNlzPoIVAad4u~* zm^-ZB;NGn!wjVcU&@~f#rVkx5Aa_o?L2T>PspIo7WXiNL zQ_^O)9NKR1gb`zhdkWh0@0l~rGpp5%b{*0?Ola4=UuItK7EK1voX)UwbEf2Gjh)nd zN>0Aq;%DYh$dgT$TYD>atJdz+mZ`1WsU2FjYu2W9%k0+e#XGxW$F-4tJ7?02v>v_t z51l(_O6ys9bH=%Qk7%7cb3(5+y?SN!?>)#jsC8z)S%cj@r}h?oJ7(sDyzJy=HpE1f z(>bRgMlAE)_Y*L!9DtP&YC{C zZEA7Sz#*OUCQO-GP~rDhJzCyc-JEXR)ZNwX(p^7`-}Tjwm-OF7*Uukr)sCNZd!cr` zrJrvc*RF@^_5$sAP(MHUM2oN6|Im&<_4Ap#wfgJ!_1gKDZr}Jf_Dz3df9h}StN*5c z&uIDe<5PY8>E~-TBRKw1*Aw*fH$A_8K6jQ~JATpa`thr7*N=~NyMDZ?+x7E9-L9W6 z>URD7QMc>ole%3$ztrve`KE5y&p&nh|3lfCz{yb-f4`Hx&n&qU2s9926JZGfV?c$@ zop2@O0F-MsyOYhrW_Q^mhoZJ}%3mP@a)TpsL=FX1z`IpIK#r)0Tw-$wh=5!oBEtKv zdYZd#P=Q-~uw>taB#ZLX%j=euR^5;19=Q;e| zPWz8I`j0vMlMa8OWACp{{gsZtmpbxSI{bc)Kl?lURgV1Cj{O52`D-2d>m7ccBY(YP z?`DU;&EbFM@V|EWdmMhT)82zl{r4RI4s+y}JN(0r{^JfGJN!|O{;f`X&pYxjJN&VZ z{%wx_GtT(G;po5P@b5c(t7E_X0(1VUwC~@3;?X>?TNB%U2-_OxMz~KXj|Ei;Zx6|H2NB&TUKgZF(&(Z&z zGycmR{i7ZJc!xjVvET00Kj^e~qEp}N@arA^6o{8f(p zQ%?PLPW_Dzzwad`|C7%jo^kYFbmD!BqyM}kf16W(mearcozJ@-boi|fe}LoP8BYB_ z9RGgmwEwD8KgY56x>Ns_!~fgiBgg(-4*!x9kNceZha7&fhn9ex*wzt-vhyI(c^PyB6i+8^ielO29{$No-E z{o_u4PIu%-IpaUo(ZA90cbZe*;@E3(>PI{F-*DdFJn8s1&XJ$!@ME0$b*tmw_Ky5a z$G?3Y|DJN%pY7DgPW>|uf1=~hj!yftoc0fJ_yZmO(@y($IQ^UJ$S-vGxsLuKhyR6R z?@ot5*s*uG!|&|$?>EqIQ%Au|Dwa6>hNbd{EJS1 zUUm4l9R6L0_w4sMlIMvvPCTA-#`AfHf6?Jza`=}W{#A#6&Eem0__rMXZHIry;oo)m z_ZwEJ3vyl#8?(mfkU+wU<4qxx^M?38|I`vHsKic8PI{bKtpXl(D9exLg z-_hZHhu_8Fr#k%Z4&UtX(;a?Khi`HC84f?w;rDa+Sq?wj;SX^5PdWU74j(xDT!)|U z@CzM&vBMwi@P|75;SRsd;g>u7N{2tv;g5Fs&pQ0E4u8DEXB>XD!?!#98i!x&@FzNa zm&12Ee6PdzIsAabuXp&34*xlaKgHp{=hOPX_=g?-5r==&;U9PSCmsGN zhmRfp8Haz);h%T-7ajg3hkx1OUv>D`9R3Z5f6L+DcKCN3{#}QE&*9&9c+Wonq~`&L zFL(G#hp%?{T8H1uIX}*D_zaShd;{U zKkM+vIef<9+Z=w4!*@D-m&5lse4oP)I{Ze5|GdM0(c!=B@TWQanGS!p!++i3H#_|K z4u7G;U*hnWJN&mC{wjyR#^JX({0$EOJ%_)=;eY7xw>$h#9RBAHA36MQ9R6;Hzt`dK zcld`K{tpiSCx?I3;h%8$ryTxihkwrDUvT)B9R3xDf6d|FbojR&{yz@?p2L6O@SB~# z6J0!GnZC(O{+@5yzQz2}4qxxAUyTmmkp#vs8D) zNQBDD#Y?<8N9CkZxul}J@&ap5C9#EI)nx{=%8FH7mB?9jw9Brwm|Cl>A~f|4KNL)? zi<-uvw)PjPy^$t5RaMn@3nq@>T_u67YiJzxi0X>v{Pmd8MVQNoS3G6y;mQbxrJ}l~ zuKpit&-P6EQCV5}dZDhGEH8iCVv;ZtzKW{q>i1Pw!i16?x*9n&seff<8a?DjCkH78 zPWmhsCKwDq)SPstdMCwR9A0O42UkEs`>Y2i|K&aIGnBa zkg~Cppl&wyXj#;?(-jw5Ok*IUSzTT8E!C9>Niu8hH{4|G38eF=v9W1%B$yGZ6;Nj$PYjAoj2_yxgirm#TE;#B{ZU^0ISuD4Z}FpG8V8S^r)D;cN(tI`jzbbvHvM7q&@ zpe4ScDH$_r)D*#NdW)h=re-T=!a&j>y|=X|6NF29_lZ;vL{b>-Krm(0m?qJoMlP8N z>f>@_Ps3!>p!RsxMVZA$lZY6cXfSD$D9{F1kHt(Tm_Tn-40CcxV5)0tzhW@yt}GGE zkI|zqNbHF<=`f;~DJK4F`f2uFBbXTw$use%?iR(Q4GEKEd0pMl6MN#Gy?!ZJnfRco ztozyhg5f`Y)i-Q6hHZ4pE^i5@6L8$P@l&SmvB$KJeax2+U5P+-?Wm^FW5B|RdPweYr zVg^sXOrl98*(S5Uv2n8bf(e3>7ZQf*nq39cMA4|ov}~ARbQx_mDN9ksEY-D1YSOgg zlPYIcFfoY5s<~E9!Y_Ja^-zOp_82ECDvn6x@@ZebO6?gy6CM3yR8B`z9hOXOS2>%E zDp+2AqLHIBTsmFLD|!XPSJPWod=|x1R1N{tHCcLV&oK7N*#wvc9GV(OHC<%odd zs#i?Ihn`ucM+;Vv-{R)De!&Q!nuA8p}thV+uDkuAq&JQBm z(74v>n$*&ysUJ{XywE_2(VDt138wSFbXHD3XB%BjN^=&dt-ZkLN{F-%V&oE))48R7 zNW)tUCKJo7VdUlQipkc*n$tLnm3REat%BjQ>6l>SCj33omCcuxQUpiH57_WrL5Lfg zCQaFO_q}F*e70cdCP~DUt~x0Bp&Pt2eXgutthzjC=y|JAS{`?Tv8T#(U$2wYp5z#d zYK`p3qnf^;_9RlA>||;&m%gF80-3ne-^OnjV*-u3#bSw|fKh>5tt9w%W5*@`^$5K& z7ujI`n-Fd!L2XC=BD&7CEldYU+{Dl4O6YzlCTU6IFR0HWMiP6@p|ZS;9-}`GD*Frr z$8yazX8c(B(GYYbs8z&IcEjBbCZMS6e!h=lmKoOOjc|V znI{AT<&xz~WAki^Nmqc7wf`lD_yJ~O__nqo&j6B>Bs6VT$d4kZknDDPN~+(^U}B0r zh6%yQ?QDKLLE2@E^s3!U-i}F@0EdC9LxJBH^m2dNRdWQFIE|)&JetV^sChHR}zF__ytBzdSQA;W*?I1l7P|;NsyI-$+E89HJNpc$_XN&Goq_i z&Iqa~qu*)l$wW1N6aGGM$_lne>Cb)Q5yTxGI zgq-H(y2Z-r7|9yV3FxOOCP}1F?cIuL*XV>CwCfxGm|}J`=;NOS)5{F7wDGiJX;|d= z-tbSsa7rFMu%*{~R?d#FEZgN?@`sYTUDA$(Q80vbLDL+nt{J769-K6SGV~J#LqlE? z$_gV(%wE=>c%v@@n3wx21|mC!9pXXb zv;W*Km`wv4=JKBl#t`YkY?ijVdyH=430YEI{d<*@$ibMLY51$k$-1Nyi+e@!dR9QA1k-z#1PLH<{+P;{ z#U+{hdmBvr(KaPJX9=d(Wck#+zjjV4r(MyMUXWncXX)V~7Sn;06AL-KLNM}2kJLIs zqeitEIdd40tjEJ1!NfeSXgbsM=M>ZPfvy{Lx8Y19XXh??$-B+V1T(Y4yi#QWyjd^| z;)B{pcT{BU;g}?rq<-B4Dkt94TM@;V$5c)nDK~E!IPblzawanBB4hNL%9+rM=i>KU zM$W|4bfogXMlR`@%gaA$Fyo!= z%bY|G6UjB#%4riuedK&AM`%;s~I{D_V}n&StePe45j&V_Q{DW`|7$GXHUdiJEj+61Dnof=Q@S!P>gl)SjBRX;f49 zU)2>K^FesuV8#(0%Bt#0`LRn}k>RyhdA-H76>Yetc7kHGVU_|?ys4?#VAYM=@j1s> z4v7;cOb7%^_dE??^%5gT=OxKyNvy3u(%Lf-kf;GK!Q?znlsKV}mQ(ecg2_vHcB3)lCQjaAr=52mD?j|J za`LIr_=!90xbrTv4JLV~ zms&X**0hqexhjvTtmN=@iLQDGj0ZOwIcv{k<@c>F9wjL~;^q`nWqLGnDk`jo@ zI~0?l)NIqZMuMScL0yXOvRK|)S=l|-o`@pLRbKvp!9+}=C{0xUQ85*wr?NxU)c$pd zy^$lIQB0O}IS&$5mgs*8rb`loNMgyW;L7Ajy44wNXk?ASq*3XJ>~Qtt6cZuV8`g4i zw{fb$(#()$e}_>%i7QNkI5_>L_2R|#2lYY?L$X}$G=7Prg z&ID6`h!$giuVA83aNVEC8OOEmL4&EL0yO=5#jLVq5uCnCEVr`q^dbx{HXRxFjdZekWS2^p9 zbb^K_TFgjle9Cu7?O|Jj$Jb@13ML(r`KJ-6uG!1V8C1rotZatG5M%X}Y^koEt(Yo{ zwvJIC7;oX2$`TgYr0Nb4Oq-M?S0{DtDr?WSOw*M$C#bGWgJglOtjt=>G@$8Q)2;Rp zlTH|ewVM>P%aDk1eOWMbxKN8s?7FWR-GXc>E8A=_nZ6wMbREA^bw%G^nErNRPnHc` zo+~T2SUIX$hs=i-zcBVxDEa5tsXg)drUDMG}?KMWuL>Vc`xsy~*?r_~Nj zRkY-lb4|_P6J0#lfkgJ@6f^CJ`s)Uh0kPM}k^d4*v@m8~ZPwN4e=MZajq&8UK;CbR z*Z-74)R;oVOFku?YW|}NX+U~XDW7Z3O#br81mWs;nsx;6w71lm1Eu=Fys^E{Q4plaH)Ml>{hE0MFklQ3Ls?5UW9 zS!UQRi5y>Yq6rCA^^XNp?`*Qr>d5nRj!=x ztH?JJ#><1Fzs1POdo(dOb^`AhJtgUJWu@>n`%%!CP(-V;ncByWxI zs0R5DG$fDZvrx00H*Bx}sfJD(86!;)U*)uO$qrI8-N@;Lkr^jZ{J4?RiNV@{-|9*flIs@5#OM4~jG1HW z?IMSgIIU+IM&)j8L+oo;X`-R=VS~w7%DW8)Zp@e$6f;YS9QJtKH0o`|Sn70-CQaEJ z%Jn~gFvFoyB{LfZ1JY&3G zPqP@qAtz6ntM%szM(&ED1h1iS^q6amoK%=0qEA~aW(=BtznjQODwy%DzfCa!MoFC^ zSdn0gXtq_=5a-_uR?hmsC-J=Bk-56l8-hucbrf|ln8`2S zMvb35Wv6RZm)3}ptPeZv=e}sGL$+NGNInFn6|_} z64n0{V;ac!1fxzzn6aRb6(f@WRKzMvcq;^xloaDS!nJixikb0J0~i_Y=!%IvW@M$U zu_|Y@q*=Kp7~RB_y0pE)%$hBW5ZA6&E)f)`nhj<>HYC9`g_v7%?VZX=XCyVNs%9ys zLC^roC?6!4gJm{#OmTKv6okFH8rbLSKZe{m3EIcm^MiSb^5P%?cqYZ z!E8_s$tp%~O_(J0E4vJ418A1x{=}Y9lkBY6B$(}~N$C3e(*?7+q6x!4|7UcI+Wneh zIvHdE)a!iJmA;7=2D{Yg=7WNj%Pqz&$x~Iw{yXMo!5Ay~@JDvhDZ5Oa`e!4j?c%RI zC^x;B=$b`G7rcK7rUS_eTh}yUB3GmQ=WW`6oQ3EehNlR|%#ukh+c{Hd`%kKzEKD-r zSpS&*%d8yJGJURSQ%sI8vhT|)x6z|FDn=+}LDW^ZuI{uvrc+<0=0z5hn9I^D8|$|P zOO_3@m^9p+U>ZD~*G<1tU0Tp}Q@*!3X56C|v&V?>93!UuTQTuZIwxO(95`qsN#*-LZtXa|#l{*1w7OJkxz*en>A_-C%Nvh#g~^8aA?0vDs! z#U%SLQ8^TJ9uhJ#MsB(DA#R?oQ|U;O^R9b*Y_EN0e`;qi$pBr0jK%qaYFhsFF)4E8G}}?)yhdrIH2oQhqaf69*vVMroLF+ zUTaSlRJkNN8>~G}MEYfCzHBxA9L1RXOc^d#m(gRtBN!X9PJB76aouWltt+H@Rqf9c zUD?lBHRLeyh>?@;qgKfms`-l7xK{gFa_4J@TtDzRz`8is|Em?)*PBm>D9onAF@Qn7Jg8bWHA1EQyp3Qu%{(djN?*PRZ)=H^nqh%VkyMs((WFvi!i}TWD33vfU^14{plmX< zu|?&~NKhgBO5<(Do_c4dYE5mVn0#U*fzsn-)9;P0NoSTFzO3IEtDLr?F`=1JudAFS zI4$V%Uf=M6V6vmCIlfdSZ%e0w=>+2IcH$oBRKX<7vLVSAzIidVpTX#?X+qvCFBD9~ z%&AUYTwyR;)m9{=O>IUuiM%{-)%Pl<^HxTKfvc-KMKD=VHGB1ZP<^^$#u+nND=N-b zOjjM^U}o_-)}Cg!^qT8@BPT}X(k!{87}JW6hK(3;X$jrSi!td?)prDwKc&#&A)-7> z+^!gY5oJb__pD>aKcKoS2a?P3N`mK)Ck%!ioiUodFDM2gfzm@rQ`0+w@pNO-TbmeD zx0`lH{?T<<#%ip=B-sd!Ji^r1`-15vL5r+kyaL^BpF%lJ^|G@4RIXvn&O7hC>u$U6 zvBw_G&CSQEoTPpJ${1bgvo0j^Pn%%E%UJS7PV!*P2F1!`it7b7>P#c2g7Ty%AFE#> zn5;DNjKbfS@O7H8w+NP8@>++S{<-RESM5I|NB&YVdos|#*3{msx`aUfDot0Rs+xxd zOTE;V`q8*oV>%WDpv&U_y>#0t0j4}B;OH!Q!r80=R^IQ(;c?k zL)wsaPC(AZ6DCgHOEBkBjIJe_Aq&lHk>fM$y2jD`(e1?Tr}#VXv057R;$n(?Ih z%Br^wChswsBYMcJ8__Jfc0_ccpw?hoVP*|~T4J%n%&M}wU^0{Rs!1`O4$@BjIKc=3 zsmFqE&bB*QIi1raiyRc24JO-YrThkm^lrA5(@7(1DspoYxx^W4FH%giG|k|Ht(@tI z`76^SiZQJ!#!O#Uh@5p!vRSU9Q(cjftgfg$K`@y{!jOZqXrHJU5{Su^;#&$V?A^nt_a ze+Spuy{1Q`2IN1tvrxO&lI>b!jP&&wITGt+>FlmD3_zRg#w@Z6ZRiL_D9rH=Uqj)mb&Y}uIK(z?r)%t?d!bg z1h5QPD{XD5^Fr>!KUa9ck~%N=Pq~*HfP&?2uJHn7<4^JjSdUhC;iv1o9QENMcyLc_ z@CRz4+@qh9KkzKW&ZbH)lJcKQFY|7t=e-GbDs4#NK7x9u;mfEB&u^;myq77jV*d?_ z+*2P=AA&{5c>e@{9r_vt8cN+O+&@N}FIRg0x2n8I%GFg~Tv_FXRoqusc|Ik0zQ)Vs zsyy%8)V*HqW%#4{+#A(i@CyDNg&krLK8Q{F7ra#NMO%=s97FX$ttYiqm^8cnbF zqBGGwm)L~FWHo*5K=&zpTtk1L@%!bT_c3(J!H7dRzRvTvuk~_Yhfc2bf`f_aW#|Ri zi;2spiNSN#p7%p!Bl8CUUVSs z{0$kf@B;ex404O;6E;2Sf}6(v3}fK^7h7MT@FO)5Kgtk&i8fwftgfSP50bmj6YF;v18DFbb(M^T{6Nl^ zu|e^$6;C5yzDS;(Q0?V1)tTH!4NmTR zkK=Pkt(RF_OW!HyVuRw**YH^SzmC|Sh5fIh^C#M3-UO%9pa0R{EWU7`d5^w)ioQHg zz98#;6>Leh7ypsiNd5Kr(?#C=23wCHQ-%*`Vh36Oa&qbnbSVBDbZ(;!?lbd<5pCqo zhQE^-VaGp^Hqi6e)OtRDv>d*J-*ai>Cho-sb-4xTypP;=6<%;Qv806H9&H8q<GRRO0GuCOh-JJNVv`ckPB0n#PsF+dxwY75O$j7tnM>iz8{FeQ z_Yn7#2;0&A*qcf}WF`qr_)C?IPoeEo?+Wkqtzbmz#kK|s)oFB3Yj+MUS*D>%yg@2g#DZ$sl{srwO*Y{v9K8Ag;sG7c25Th~d z$;+6(M^<~`*T5;6rSu({;9UHcx%YqgdNA0X=*s;H#&;$2AHCcW)m{MRNgFrf?>Xp6 z*-R{;Ieu6pJ`CFr)Ofk0xJM?E`ZvkF|HJ-h+V{Apt(ZTY4-Y4HtZ5nW2)jAveRM~i z7r$8N1^DJsf^&%1D&mEYndSJ7Kf%`+i?gYdH4z%hy_Bz4dcmIXl!)T(1xAUk1>-Cd zQ~W8lUT`w{f1=GDk=Y3uN=V84mN5d0xc7W;?gMBjzlMgcMI?q0tQNp&RWD zy@1%j=T0X-e~rEEkv)gDT8PoPto6SF1CPLcq42riK=(xVPI7ewabbP%ZeZ>}1LQnP zhPsI2--SLUrub)6dBNT2Q@kCJrG(P2N!Z{=5`uB)jt8gs@&gkSxTl2hAvB_R6Tv7U zC8BtfprQi}(T^x0_ulKQD}MyP1niq&7jfUgdFN>E|A{}8OcP`N560&;eBw-&gL<#y z?|;b8cPanHH`d3HwK2ek2kwD;;30Sb?tur`4Av6wEJf~5WFEAVH=iLN-ylYB(%x3` z`&II^jU0LiyB);$_l)m9i6e3m`rdowQ3k!wvWD=ZC7IixuaL*5gNyBx$d|v-htJZ6 z6nJkdzGR5i_rS52dmX#98DcY}&i^T~Mb_g-uQF0zqy9VOE%$-Q;IH=&_C}F8hJwB? zK98yQ;-BHeS6Tlm$dT9ai}Q!ac_g4j-=)oeQ*L0~Zlu)VN65I>(`Ezr>*;4FYv_;Y zcNO|?)ObNZ`jn^-8$N#hkG2k?k8cu}EZB?e?LKqoN63B&-zX8q!*2LC@>gT$lf>W; zjF*&W$fy4h8_p))H1y?6axnK#(*Mh8n;+K-J_Eg-cpr-1VdVMqjQ0}m1Lp5D%+)LC z7kmisf0h{j2wCnk*oY26o^z%@f&O7PfX}>ue{=9_8RKyTnA|Vt{&31t?4TdsL5@h< z|G*w+^OzqF@^`_;-|KGqT^`W#4({{j3M z!8+vs#@Lo4_cZIoGvvqLhz&FcAO3>6MdZ|Az2`s6e3xdy5sSQy)}3hpZ$6-a}E2M$c_Kd#`}<*MKJ+H!Zov=q zBW(N6kl%YFhaaBEOM7#XlY0CNzmFdxhri(s=zW5E+VjpM2A{;Xv@JZ(pSeeA`#^lf zUhYiR4Ep5J$K0R5(aStde7=Xhk7EPhBk`BMMDQU#`S|SJ3gwwN5T9r}#=k&(r!9~6 z1MG#ANHE$9|4Mt}hty*yme^6}i9T(H(uPp_9Z>Im3O!0l@rZFShjm8!f?ol$!E5Ah zJ2|q3wS(uti1pU%!|uhzegS=vAA#e!Je*EUCbE7^qWldXeg^h)`b#bbpCx_^(Sr}5 znZMF6N<_(d%>D7~?f;{^M_=!wzJ@iUnm!+oou%R%bBAYy@K@|9m$Nq>$2xfee!f5) zm(kZ_$!+Q)WCL&ynapMQM_owqghu$&kKe5SAtmBI+#>B@lj6V6xoU6L(*b0D$-I;` zW;M23kvkNho~Q3n?=ae+_|FqpsCPK^lt^&uqeH-;flx|FiKq`m=3wmnjeb*d&*3}8 zPyC}kJe_!Q=JC%a=4a8*NyPZiABE>ECal zE0K8x{fVpr{Gg8y9wC=|3AzGZN{;#nxtKbSxC^_fURE zolto1w@~UcFOl2)I9T|1#{GVLcXvr`#+9{deuUUX#{=O^$v`2IZfX7F!8Pc8Feesnjalpm| z5q4guG{NtHjgicW=dihvHvWq&C9Xg&plm{JD|V!yJ7-Lrgolf6yj<@M$l%9t_zG zz68?Vr_n1%|9s+eJ$ZINdGtea=mOTz$KfBNJjEKqz5gWnNXa}w`3rKllUEmWe;4=#A)nkMdpgDbe?#VBSsCQ*v9O*bIJ5-9^Yto4?2QFDSo+|2gyl>~rtY zj~xBXNxve>@4&?GA5gC0o_=`ZC-*tz<6mL(7JRxB*@uy#@8M6O7sB5S#g_m-J>r?8 zjhKG<^f$(?PYKWsB*x#MKm%eO&|Y{O`uLrBi2G~N#a8q)^ca&ISWNM$_e6*Km>9+Q z9b?;n3OyMw#wSPd>5Kmt>L?*L0x36hPt3wQu#2ye^ozD*WCIyDDVM`Do<1e`4!raq zJQDxlBZ_x51-S?sN}>P3x1q$y$LIJD&=~AiWQbw3g(7XsJy-^Rqf6l#{{ZUA_O{e{#{2{*#-b+!KS4d<~?41=NMNfM0@r+6c&_jQCH9h<$W1^;ba|GmjDo{SIxA zhZ%A?N6ChFY}0lf@6mH6;k{QnH) z7-Wt`o{~Eb{&@Hk;3*koa>(S6$x!c8A5(J3WT^Ku@M2>Z%C6uPe=7Hs7%V6EM{++O z965gv_-WwKxRwI->!8ihQIsaicJK?Kv!Jce)#y-se26KzF%-rrrtJ(RhhG_d^1(Bo z1eYRy%Xnckht1pqirD)&GSJMPlsV|k1IM>aKplLB`*q{4DK7aUqnGZ2aP`kJ`p+>Iu9IsIcQADNS}6vN-oh>jBY$0o*eWgp8LY%hfiBM z(Z^Q90ahYT_ETev5Ged;s&Awz6q#vxM=M($+< zmpFbJoZ=I&3^W7vp%Lpqj`O3(S`mD_g6|tJ&%XtB25ZA1oZCIl$Y)k~nbW8nQR!vC zyzf(gKsoaRJxX*d^a#%S%aFSfegON8l)EUSSR?jC_f%}b`**VjQG(y%4>Tk5iF@xJ z?*GX>KKM6bV;S}-;qA1wn)CS)tTB|}4%%wN9_Qg0-up3f@ZkdL>3=*18(+fK$>>dl zl5gQg^fqDhbLi~E^T-bPDtXvS{|`eSo%oCRAUfnpG=ME)9*w|0CC3^NUJdpiF#7BN zfU=za(C3)*W(*(0hm?TxVeW^N72H!IIm7;lvXZz-;r#AhjlFwm?{~!EKH46QJ;_g* z@5I(;jB}@8<5u{wl#q7uEgVTZ_jCU;`d1)Fi7CMcv_lCPtLS~=$9RWtWB*EYDL(e& z1z^yC=Y|YxesBi*DvG_c?lcjz5pV7CUGfuyu zzq`}FU(v2lAo=@BaijO75pnY=-D&XeXlN z80+{e^no_xnaDgt{bp=2_PK8$_htGZ_0#Dq)PIlmP9+CEM!%pz1#vx{{v#K^2PJ+n zbus6bkdoV<_Q{R#50od+#b$UWZNX<)$AUlNBA+&RdVzi+N9)mrcietd`KTI zp#5)xpN|gfL+$|TDLy5p1WT|XdjjVaL#WLe)m>TWmcjq=bGKPr;onO zPkfCQAqNeXqIWD~aU5k&aEecU=9bVF`I#G!O`*sItWW-P*c(ZEJLB^gpyXDt9$7iB zu=jZ6tG7MlLtj1m9ZTL`!ac>m7h8YE);Z|>l=1qij3;(}Kw*#ZWc(&0j}PI~w8dH! zzJmNF^v|ZSZUkS&&i~=d*JzL8oew=1x*0u6MDf2tk+p`p2tEG->MrD-;uoM12hTOBX!8(y4A9D5B=mTfH z$w?jSW{!Clei7YkuzLft+(*dzyKsLUZNq04<1794n4i&i(XFJ&UPG)r`suT_csp~y zBV(}-@sn~W@uh@2pt}c@z0VtmzubFbj}o$u#+2Y-=rQP0LdL`21vzX5Qy4Ew#C-@B zQ*yh4Q@p9jP$Ek1F!;kM3(##wj`0lnj45Jo&af^3F)Vt(b0 zga;34-#d!(S!5`|XV8U4$8nF0e>8O&cw~YTkOBAE-+Zw}T@EawK8K#)%6%I)Cs9WU zRwGYcEV04IjI=}UW{6wPM;@8nSPHR-Scfx|a5wa%4(fk``va-N_Z;V;U;GURly zJ9d%D&8MFJW+cw^)epeX3Gpqa%?LbTZ4Zc7z_Um&4V*Y-W+8)ppRvk;`HWkz2n_ok zF^s5>ws6j?tm151?PUh}%=E{c=PJPNtUF-+f@wvnXKA-zZl@|yPjc!5? z`QW$6lG~*&|DRI7SCB4S^B80Z~C^8yV>f+w%+S9_|X?zJC@H` z8d(}!y4c#u-Du>!b%s7_?fRAvEX`ONT6&gkCujM{(%4e3+t~9hUHo6e2bK?Q`>WiM$6xGzu|pr zFR(OY>BKFj{%A`>i{~t@{+g+uVQFmfE!N*hEv>%P$j!ENt)(swNNzi#X-wzSvM(Avpa8d(}!dWVgNx54^hY4tgVpJD0ImWGxFRxV>{Xlc&U z$kNzSZ=y z(`KVL+tQ4sXIXl)rH@+beZ$B%Tk37H{j+qL#Wz}-vox^v8B1fESN?a6{%dwTGL~utTIIZIfw%@VkU$gwg^NfA(^R^vJ1B(Zi&sZ8-T78X?&sl!r-G=YA^d3w7-x@qh z?AUm1vGg@dXaC5^-EoEg?cUTEcVmPVGw zmU>^X_AL!8%~%>*nzOX}d?O!OKDN~RqP1gbU}?tEEti}6&9R}O#dDTMmd2KPU$XWr z4J^%A8d{pObcXfo=)}K7&hm?YV(i5h_iwQNSbXu%49^#}^t|U=8d#dKG_*8lX=G_^ zsrO~0zx4`h?@B{wSQ^+o+Gwe7<+oTrw^|xlJY)4T*58c9bC%CpKC(2n^c`!*zreJ= z*i!E+hTm-MUTpEm($LnwX!)jZ8u{6lW-L9+(wi;KS-nRS-1hIqq@ULsJ4ai(+0uI~ z^{zAZTko{(Sw6P?^lvnHuf-$l|CVPBK5?tnd%)1wZ2hw544=ElQ14z#FEq5-(k)gm zlhnUx>X+I2jh0S)(BP4k+hYC6C3b#q>#e?jpW#DG@3C^PCH5{d^?{`mA2NJw<^8)1 zzt+}ow)E)t41TfgcknyI-(l-}|7ZA|#bb+CUuXp@ce z%~mdB$0N3UQ_jfEu=dY--|)E)41ILO5jwA$e{T8T8rp05J1q5nVerM4ZnpGMOZ{J( z`n8tcZ0S3e29c>h%hIiudiNXLxBUsMzk%g@Eg#$QOr8Z_|KNiUc==oX{bqxV;FW@2 zO(T?K4Cm#p;`@?ZnWqf(PvUu#D}GadkXP?Z{nOb!knQVk?b^F_b!SWS^yS%(Iei`d z(^}>auIu%Nt6b7OU$+^|%4I#;ss7TnOV+fuWtVifcMj>5X>>{d{La2?+kncI zHnFH}MOSB=)B0d{f9sm8RxIopVtZA0ThF?6J>8~_!?FWwd)gfbTie^`tDmQd*>$CB7~sLS0r9GI z{b8-W!&lDf>ucRKeD%V??zZ7;$^?nj9YWMIy;65;lj>rSNk^VKhgjl zKDf>dpc|DUucx&PD`(?2twnR0ggqp?$+a~^rqnQ{Ld&yjy0RNP*Jrn7>F{i}o%&V1 z{3^nb(50rBpKA~6>{vUnthKwd&2>x0gV{aQ-()s4O;!zw?d^(FmBw)SL(R%Z!Tt>xL)cIivE6D(_TZdWTmhoNh2vb$)F{8J3uwNaP)A&aBx z40Ry8VW^!AeVqf@QXVx=m%W00sN`-UhS=_ggr<3VzNTbI6M<3@O6r#Mmxx2Yvh+w6 z)R!EWq<&}!lA7VhCaE71(B|p+m3Uf9$>pm^V7QgHP-tH14Wme;-ndzs z)(@Masl;&knF^Fj&V1#tnVE_VmzL?NcbIj$uztAIERY!{ISVC9re}e`ha_l$5J#6w82o0CM`IB_XWwoGsxa7@?43oPB0wq&7Utj8UQBb?>vqfHT__QvN z878aq62l~QUSOD<&I=5m(giZx%IE^IZ6$PGtVBK+RF+8Rd~J#4IbS(Us^jo1-CUrT-j|+6GyhhTyZ5zOc+O`#H?{7hM78!)Nqr-5i2!A zT#;d?i6c1NTv;~IXWsdgoF)0%;pRswG0e=!*O#0VY4wL3V^X={lRPgnOnRpR!z6ZE zKTJxe^}{D~USwNooR`{G0;f_XQa4{$B5Bjg66u=O4U?#8{jf`XDluFVrvjzYH(xny z;-(^L$`0^S@_?4?$t|4{)#Qa%QmwC$ON-dgQ}SoFw%$SBUv>_3wssZ0UKYH!ucs~B z-@mkXptGmD-~#}uBg)y0b2k;fRu#CNk25+8-oOdg(=QDaH@S90TWjx7xU;|L!x_=; z$POH~Wd3q*Xn)kU`j)ReX8+^TkQCJS4R#N7uFLM7*0m1wtm|y!1Ey6#v!}Hj+SA(Z zeW=umfu6qXhl|bOlb{c^wX|;=GIM)+x<1q?BlJ;x+IQxM8k0CAVsqL~9^_-CZTOc+ zf7HfRZh5vV+uHw8%<{QUZ);zcIIh^x+WQgwC|3CB4ShuOA3mTGx^0eV5-~oZ{E+_i z4)lGb(Msh$+Hk4(M;WiY;75z2$`=p7N9o~52)$a~cEGxO=JoV$l6bY9w2f91u~pq& zBABiv!_86|ty%khxZIKt5$oU7-*R}*z>@C8S@Ia8X0cw)y4l**!NSWTx|~nCyANsY z>y_2CV3kdT*n8LVesGxD1t$--c72%Tq4n12&v~uBv=@9vmw00bI*D}) zg8k-mjJ6&YSrHpfk?NuC%@ZxUC!edY%dX3wtlD;5S`?8Ey1)tRTl>27V`Cy%xh9!6>X{uyqB2d30KRpQ`j(`|j3G!-_p>rTii4!k zzB7mDG2s(;6616y#R!gS=7CxJ6$3=W0njf-h-WdNy)K55SQMjK`Eo$^<}q)5ZtgjRcoHUVfEa>HPhtana`2lK|0EQ)Iv!`*R9JR zPFjR(pwBIFfB0J+h*ta>B^$HQ#Pv!5GM{TQN{Lon_eC z_|!91X`a4T_-QSDL;YiTEsvx6lnvfafM(&FmDHK~Qf11ApRW-8OcfxMM{Dj8*|rWR==@1df*a}Vy551jUn$(%lb&Z=(zb^2 zSOOp}77vOb`NT|QQU{9LO{$$(h>cW3m~D8#N&KWXURAJ#<)hricX+y27Y#)}$9n;b zZ0m6%?|<`j9?F*W^{naa;!6$%%XxkWOrOk_P1Tr zn{)KCcNhA%q0CvMJibgJHeBMXbJxjOUrtmne(YAaJPJ$B#BZgm*qck z$)`eU`>4Kx7u&)fE2;`tSW{bQyo1ljoI#e z0&&k9nFV?hO*(JB+c&45eMMHc1$})zJk~5X&*93Yy(}$SERz*|bJi+tq)?!QDqBq- zuQEQ=Lb2S38WQoeAH7|zou;FuT`Z_CHoTfMaC^UEr8Nr$id9>Cds*?AD)cOMq;sGr zpY*-*m8jP4eEuas+h9L0LF9~;Dze3Qwrwa(OUYsRUJqY{S|)~4QO0?}#?FDoJv{}R zfJ~%p-Vej}FYLoWey6Y}*&&C|zWh;3Cc!G+YVobcYEciVg_{~nLF((imuYgxnNNU9@si{*^Y02G0wT*{lZ5?A}O`CVm* z-PQsPQ#p;-cLmG2@lTvA^iMqKFOrjk%KGeKJ?(>C1>>nn)7~>6`U_Im3ehDg+Sb#( zeq}MI{}EO(RB&8Zg@WN#z91u%=TI$=kIB+wF#XbVA+%&>-VctktV<=4{32+C77S4X z$qVrvzdpWPdV)qPbzzWwyOq6{v9({EO+0Cy-nFJ*PHE10!lKIx&u;^*9Ua+r`&PNA z#!9d1?j2UXrMHLqS}+^C)^zu@^MyqAEcrHU;*jhx`R!sxOP>_vaiZwu7O!^Kt>vW) zr?XA{bHqs6zOE~)@*QfPzNT}dw!<9k%jQGFE6DCveF>22Vr=nXM|N3jM_vr<@SalW zLX0iVpEZzMo+a$v zQc%2J(Ymg;E8jDDbCKQHTm16EzRk!#4~bM)Pe(r8*L3x?^5SoGXTfUTo9&Zsa)>K# z)xn)Zl2e;MaWzk8N|+adMJHeqmRBIz);>*Es)}-!+~E;DA1a>Yc(jx>*wC|{KOE@l z*^q{#dHQOt8@7T+>=JdxU}tw*U-z)JZ7gd8*`;oa@}NnRC-h4arhZs7<;ALe&a#kw zgg-h-Mt!yJo-5XNuE~$PsG40>zrvHs_DLQN_V&w1Rf#?gx3iqJ9wKgVSe(Vy!Nqz}kQGliUNA>jOha-u2>Qrm{`qu8Y>;f|qJTV-`V`o}x{Irz? z!$kXb>`#!+7aSClNpMnjQ<48he5NZdhIHggRG7%S6-k#|Jn5N>JFXUwQK45(P2Q_* zR203HN^(U#C{QZ$xBxHmxBz!N=9HC=M_Oe9=~m@^c4`VjSy)r#Sz$$yYlRhZ*lcI3 zS;wN1dbMHoXS03zw*ebgA68h~Cx0>2HXyIfinP>JzZ@6a_$VngmbAcTmSSda<5-?- zY-tS}vw4?FE1g|kx`r3;wY&jp<1Y~TQ%3o~q44x!HIu!!_`tEcH8}tlwk0oPdDl0P zo!hff-&CfR@({7KcoP-JmKHvD*aq2ai`z;&!H(bAo}IgC0e>i`-&shOGdWR7CwLqz zdh|_jJ(i@ttRCz-X)gPBXO@S@>{0UfFlk-kYu3Vrdx*x0zP6>^UHNe-FlzpwGd0O` zPX3uohEF`5oA)6VO#3OTr<_wnFs;?U+euKjqeYc1%9U1Z=`DDVQ>?Y7tMEl)5<0&4 zuo9nBYx)e#ak_A7)5CnMI0|*F?6H|xwAV3Oi{|l`Eq}c3A6(6gu(pA`c7o(1wvKe- zPr>ACAda>kMa;o7b*}((7XSPi)y#!dMeZvyRGQ6guId(KD4#3;D9Za@(!() z??lL3=oK5Xy@lIZawfLNSteiFX5MZu&&V9;8Bo-V=U-}E&(<=xQhkY9c7dMC^M`{% zf3PXvopD_)2Ik2h z?yh5d&F4JtkV>E(ZL$f=zCCAsYiE}{nxvKro@0yOICHp6O{LP(noP2m&VDgju!D23 zWxNWW&H##@YlJWQFe(8`K3SzLB4r=8iscR~c;||Ywp}c-s(X-=wu22pl4r95Ieo62 z-?Je<)HINPGAxh;$BzO)79#sHDeZxrfn|-KpY3IBNMT;Z^Vv&wMhfVMC@lLrbMt$Z zIjV!_SAkT>d7aOCLDk8rMVebU#9n82@n?jobZ7sfWEGN6*LnsAigK~gn64BvQ_Ue& z`Q@qbnY`$XTUen>MQW|1=ruu02eY~;V-vt}q6p4AARpWF;!;1FYweObknNKfo@sN7 z=H+kA<7~?FEjC$V=J#|L&X=Mkp|iiJwxdnHBCxdh(93_*&RlUm#_VWY+1JTyp}rR0 z!ld4mcqy`qy+huL6n?Xfppa30sT!aBu4^s+vR8|)c#Gx@k+X|y+R6Ux0NVs7{d^xe z;B?6JX0w;2HWjt4YT)-RKt&7ztFFfkE%{MoQJ1*n|Sa^U1xoj8U+{goRsCG4S8_p z`zr;n$mKQs{Lb`ak`iOIq{I;E>v?C;rTg=G*a1n(EFXigQ%`G=gTtbpf>&)rf}ugo zr*twrI+2LEpPN`7Dsxu%6EHdaWp-3hQWRU2h8qu~b)9!qxC-gN=+nyqnp5UIZEc!IzirsQx01E8r&pqshQFh2S?XO17Krm?D@$uy)zP+4o_6PUIG+;aRf;$CJYM)k8NN!{ zO=1+-<%qSGm!GnJww<(s-!e*DY@R;Y&8t?sCl?&p3M-1gxxrekW@rU_vbU}t?Cj!G zb-r6#VAmC5eRN;7SWD&wjD9g_dGW``MFOUd_cv|wX^-2IF_OseOyWG1mo&xOf+Ab( z9PoyFte2>+k7!6ohw8 zg`ey6>no{M-EH?=&c^P6p-YRS-?*u!0kq2U)!;yTj#=2YZI*?PEw8PP?_=!?X z$-^ka+&Pdxob<14ZO^AsM_+c6<8*3IRtPb`yM*E#Nh;(Ul`@O-dmOKWI^>7EvKtHD z#1T7w)y#}@8o!P1Ds@Fm+>h(W*kFHL%9Sp#~>=`*KKM^wo3Z-ou&@b;NcB9P7u^;dq3F zp0yAQ2%VARtFS1W5GcSIplOUm!W`&T>T&< zYg9x}QLBrIeOO<=#$IP{&d_#~YF9T=^8|W$z|LM;3Z?+TD*agBigEFB29G{)Vm*h@ zjk&LA2tG5LO!p(sPKVpO!QIHtTnyWo{{3bQlJC>W*TFo`3kCMV?(ATPM7KqgF3am{ ztj<;gc!I4j;isGs%ssWx5%$`;sOZUlnI(Wb_UylYSZpmITyKQwfVmVz5+J@BFd8b@g zskvRch-H3HSYB!Zw>7mD*Dptrq^~XC04uZYd=hP!h%`qc{`Gvs5UZurK~y7|M8?>6 zZe~~*O(;I#)`Mx=Vx03h{VCFCaRviPttrr6y}0MzLxirNL8V$cV{yyGT;`Q_nrw~P znpta9W$!9=hm~M~W*-tV%fsF`xWAU0HVe&I0~zryAa!|lka!{a{Gmw8nii7!>9hiN zmQZM-wyihSg|~j0sry-;ZK_vW(sQKXND zP)()W<|O96$r$K~u&*b)AG7Np>an=@4!LcK*1`F_i-Jam^>8W#7E zRYcDj2zv=xX*L^5+~($7DxdzRHP{r}aH}blbtl1DCB)aP^u3>saa6lL#XRy+ad`vf z@31VWl=Tvc-Fa+1VAqZ(1Br^+;h>inf2i)q5@#UMa6Jn5k@w4CNG)Mf#-bQh5jO-U zf;75T%g;n2S8s=CC!* z&6EWioO2z7qm3i*SoCsjKrtCZCOjPg?m{5iKV&%&W`z?>6H_Ll2L>tuS31x+_bwfsR0E5qUP?tmm+)F+gi9oO6)nO`bmT>;)hIxgV_i+d z0|`E~$My!J+qpnJ$aBW9+`nADoqnFqWf7AU&btnnWSh#I;db^pPXaENVP%;lg;eat zV9W!IHK|k48%7>(M3h%h4X2nYCFF(r2~4?tVdoP?$vAnrSlBGA>9G%WGu5hM=O5WV zx|mPWxcWR7g~61dPG{_MoT5wYM82_Je}auaeqBh24O%*bJ_7L9Jx$~W(&Z?^MQS%24kgBTU8m~ca<4Uc>|-FzSKC6Lk8gTxUq% z>c%Dq@d<`$MdiYVDk4N?J#JXvI;S13&!%^JIxVMa{xX~LoiEoLSzq-g%N2eiP{wys zG)-X9{RahY7cQ+3qf9=l90&+kzi>q`C$+|gTZ0ypl_l<(!EbO%3t`qHi{|L(SM1dG z(n^7)TgNNwYY@B1n#L<@EYuqh*0z@=dS5b3&DC>?UBxeP}Rd)F)-wSVaUm@k+Z+=q&$~9{>@yML=E}AKkLW{yuE45qzAi{!PE4KqA)(?w!I^Q z5qy>OJ~@nY&#?tvQt5@?K~_59OFX0`qTvf0Vww4D3$UCH1WN2Yj&UnLKQ;4HD!qz1G0{aOd!1)(0oFSBwU&jCwC+c==2u%Js zAON?8$5{Z&;9j_H1XKtSR;m=h9nr9?*pgE{*-R#_5oXppLCORTi5LE7n3R;lUIttV zzW}@o_ZzQQR!G&tu9Zreb}-DHBC0Ph%4+yZ?2cnd%jJ(fW-%=e+0SSB%3>Gyrpn(E zL5oT~Bp6x=e1Vknyf#=a5xMv%1AHaSHMXn2`XD75*oXUII2pouC!)N0@*>QMh)`HD z8^Jy|w!_A9$dW{0;jmm2oFIq;z;AcC{;q?mNCDQz1<2f0;^Q+yt5|nbl5D@G%OwMz zIXVEej9Xgn3NO0FS<-Ro1N=Z`+|Y(~5&@)Ok~LZ0aY>b+xNT$F>@^mMxro6HF^Lqv z>EdB!Dh2k6%o#hfGi*gd#1`edEDL@<5+Dt3?O|%3I~7a-;GwAaq1N8S$QAk}U1?t1 z9tD$!*;pLZbJfOuCb+;O9*p6QRC%}LW_eCt8A%hN1%4=@@(klKBi6==N+3^&4i(a;*}}uU*6s0qMQ-l;6qpdb)CL zDnT&jnc9QKCz!;^RYeCx5~>NWbIAzfYP#93GK^pP#X%Qb6tmpTt=){2uQJQXZtg*@`BhK^nb{FQh`i(sDwxMB zAJiD)eIA5UKm{i{sg;(w@syaT1q=k8oQwQ;vy_#rTL!~R-6yJkS?9PNfu7H!X6xfC z99W;{)|5|y;FdC5VB*9RfK&(xjlK(Wz1uz#6j&b`OGcIMMvasJnp%T_JU*9N2;z~UrNRc;!gA?y@bIM%t< z$oC?*0r_Tt<&?BQBtDe~VSbBUEi+!RJJ{l%C#wLrCC~j5G^W`~f)r#UPzVsqcyql; z;+f>Cs1z>x9+G15|N#>EJ_+}On=tndoKA94$dfD7w3tP9~|EVz;)8C1++aM1%n z0udAU72@k>6H2%?H|cwLu=;YxS$46Vb|=vxIeuXXm|YtY9I*=#=8)WB5G2@r$G}z& z2wQPCGUe%Ji8!>me#wHYm~UghTH^1WzD!)8;8XWbGV1*Er#MT`mxu4rpE(kayg6^2 zZu~Xf-0~*XJg=Rx+i|^G-hF(7wG{8K*^7<9A;M@IQTCGzqQ=+a~rm}JR#J-qzQU8%g26ms*`@4Q&h z?>j?)7wL1kAl$pFGk3f?nNkc*^*jO4F{VN7buKEjc`PCmm3|hs=Dngsl9%Dy%CRqa zsu#14&Av0naX4Mz_Fs~WJ?8h*FRuv91L$P6yw4D(E%|xDH6~~2wohzz=8_7$y)Lw7n0TzOeuIvT$hJQ2l})%z%jbE z3kJZdp3{I&4(3U|DPXE-2#}SO4vO|qPHJ+G5q#tB6Ny&9+wF$&DqIj5!Jzr!Mr##@ zr%~3scd=oS@uvrI9F{M$rB0*-=KS$NXelk^lN$L^=CBVAqG0{b%pYY@V({{v@pl<@ zm!UH9BxP@w@bWWAUFWawYi+Qhz@hto+(2zUp)6ZWxRW%xd$5%=+Cea6PEDH=?1g?j zYE%`#2MiwWh=TTfixwezSj^3GHWA?Sr#TJDF1?Fm0)rTtBGYg{ zHJL?{`j<5)@EMQiQBE*d4i<5&h+KwU&CYf$7X`Ci`OI?lSqsh!Vi{Gns%-P6Pe3}* z1(h?MulKq z{ULVc+y>7>6-UBQZ4(80#T7y+GptJ<1JTIcpgH|I9b@*In0VNJ5!yxqBF#VT*vYg( zm?sW!qX+|2d7g-9{a09^BfxJlIJZq9F~iOKf{2)or@G*oo^(OfXN~=pAXxUucK8_r zSK~-svRxrxEw)glxI&_QiekUl!3DQ}AHV0Gp&fC_)f~IZ(gue66BkMH{86ARwp9)0 z?cX;imYOcP`R0{WFRDD1I5PCbr6J7Hn#J%QJ4Y~)fnw^1?f$~r@ULL7ha29bPq~zV z4ethcKp9!lz+8hALDDvzz7C+0c>uL}7xJM0#+sK(QPj!HE@jVsyBhG5h%@PL3SA zF_L25n6XlYvqvMre=M)7&|)EzC2z)XXCmw5;r(16(WEu>Uam7HNeVV3J7nF@(7;=I zy2qMw9H#+Ep_^;09McynUyLqf1L59W+iqXJ zB(I)aqJMnp<4~SdOKN|_no|N$oM3*Hp>M(1#+ZGK zkE9x&zQN;DuU~Gtg>o#uFUHgNl5iQ7=&w;jiuO=+ zZ8${4u$j0*caCLg39|D687W2mL?s{{D;b0}!5IlQeZ(TPtw?XJ$fV%hg@~38!*UD2 zvPM8Tml|+_W;w(6+CD)MU_JsPffE;5iV(cAlw(j6Ad{|?D?J&=?H%rIFC(m*7QfD`-;3pZFMd_!OLBGrV}C zBk{3{^x;!r(TXVN6hpVi1Y6sWPaO6nen5rXsHqVscs*q~lJ_boS!KPFWC*0sH3dWIR>D1O> zAmC71N!uv~d8|rhk(YpZ7i}=~2O#DcY{gL{v6P5nps;A2eTQVc*pa`dTuhS`O#LMhrUUwq~~MTa8AI(DhZ)`ByjU)V?1hu z^E8fY*>60raeE97+Tnni!&T=2fk$g~6l*x(PVeMxJ-pzD_@S?jp9?Ty;LlRRsw$~2 z{3FIaeKe2&>rG7kv@KOpuGNxTHgBiqRU<1@(HFCu+dP8vkbE^Y?Gy~yw8O3zGLdKU z7TQIfb7rY?8CYm8FbjQZbDXruei)WEpUYe5PfZCwH=%!PLZ7rV{iL0{C++M#Y3J`r zJA+T!IW$ia+AKb4=kZB9lTX^YJZ$Iku${}pb}kRwxjby=^01xD!*)R)wsUFTMz=OO zZ06FuqGO1S2aJK<=Y1Xnb@K~ec8HY{Z3cZC-sH1xX$Fr4(Z_Z|#B?YI%&MJ1GPO12 zRUA6qrMk@HU#OYBF~7E-_|@co@)u=&<7K#uyR$yq+TPdLA|a!Ey%f zUwNDfN5*eddB52tv3bK!#G^Qod#o7w=b zvM)nYSYSE8J>LybPfjipxroQ?dk*FWjueS^cU4dO+a}#_CVMOSjIL z%;599#3N2FRV>2RQ|x>c$}CuCpj}~^*$w8hXNw97O5MxA;F{+iwOFKb{bgcG9{|6Z z7w&HWo^puaSk%D!2_nuELs4+u0($)nwOjZ~!h9D5l9H6*sZk1t4#=Yvd?E$Ry?Upn0B+*N7p4IT16$y!UhL^{_Ojl>uSc)cKortK+cLdP2m>UT< zW1cMIXsmE-K*c68_jC;4ZKwzb5f)r)UN5A7F2(OBO+|!!^aVltI zyOJVB0}#)HT#48tssuSx%!nqC#VN|BbmW4A+c4+J*81i) zU$h7cvW;f(*i-d_7PC2L&`B`P5V(6qM+W1czLtY{&q>1#rH4Xw$|>-SN1h7;4)0Vg zZj{Duy`j_Ed=hKo7TW4YP85>@1ri}cPFWbX2eTv?-y{L|$P%@>!<&TGr5#BkxVH0- z!nL?DuENRkDrZ2ydHJO5fGmBU5?Sa`%7%sJ36X{7`H-|GKGC;<@tw2VYJ}ncfs6loQgu}|r>@drEm*wj>#aLGtcvUT_ z$sSy`zSpi!KRl4D47jZ5!rs*`){NUor|Uc!!AratDo<{fDj}t$&Z7PfQrFSc8yv-< zGcI_uqp6)OXD3ty8^Vg-4W@8qPd=)+O^Wu$2kw*H*8pHZGn0lohYSkv7CB5zunpK? z87t}G!ADBRCVgd)NKbrmbu#cYFq*_GgelG|?bFG+vbdhZ@EzTFJ=dTN!@?KHHJ-gnk9^uN=XsGijj+okawf6!}FKZxw&6m*f3pJUs}hlk2tu=MkpN@T3BIEB>DefrtytwMuDR zw>jaN)qC?gab|!cC)OzqNsQZ#C0L$CWQ8>K;+##;2p2Gv*k4JYfQKA3%~db~!ktL& ziXtvkqQ(>%&uxy@SiqF4(Chn4=8-pj&rk9^MnoN}dl&MLYv4(kpe``?z}86%RmNoz zdQmkTXIbBiU@?p4JU=C7@#3v&4hbzTi!SWfAi`8x(kl}=*6=)jpC|AO?gh&m6{=Ac zSS@jCo2TfS>&xGel9}%d6mQcqFn13Ii_N5*WpS6g6tUFUCI$JjC4Bh-7r4zvXAl4{ zF}LTR!LQ&BSPA~N7BhG_0|B|0iq_2b`N{Qib;;`GsWF{>UtQF)uiJ57D-@3DWZhd1BsDu&}sM;&aZ}Y9c-MKY(#?X@=waD)IiBZc& zHl5EV)8AGMW4j^)M(&h5xrKaeeI62|IHdOQykhk5yu74MxXeGFO>x_SI|Z&k61LZY z#;XITH=GDhuJVdbh!fbS!G&_-Px;4>qLI)lnKKbSDH?J@9A ziHo{sftk#p@It?Y{Y5Xar({Y*!G{MbWe@F5yz!A3#e5h_It+`}-9*w(Kn%a#T`lL> zz|A8H+Y4e7V&}#VK`T7GED`r?vru5UQKL{;Sgt}RMMJN^%5JxW!hLsu)*NyTx!i*% z{DTkE9Rx8Vy%QuFRy$6gZqDs45{b|jKKWfjLW^+Ea*m>R_jJe99>!EiY_;PcBq?w4 z+>7nv4Y=IkO2!>>6`k29xUYgrYWIStD2D>{d9s!O338P84VJiGzPdH%gT%kaxc@8a zwRw4XK=?V`ue8?W`ALZ=DlgG4m{e6~3x@2oS|H3-kOCt^T)ZD%;8OgDY^WkcOYX&Z z`p+I%3SwOd!ag<*^>}pq4b>p@Sf;&g_BZwuUeS1mTi1iu#JB@*p&h(cGH*~@-EV4p=Ca; zQ7t&J)4W%rGpdl85M`DV!rECTgq3+SGz(#^cn7yx9P}0R@3(j8z)#SRCsj(~EM~#i z1x$##k}?-WiOFi0m~6HZll^{5Om_V#G1>%|w%}B1z8o6$+Yv3L{RtB97 z=UKOLo@L`;X2xawF9>yAcIP$93_jxT&aR&8({e55kj1czy(X+%aI*`%*b~95rWi$V zu>`lkj&-vI^PBXTN6wGs5QJd+>;RmqWBw;zDUSmso=Kbv zwanuUx42i?@3}m)79g{0Y__~QJZ;NM*r<*!Ha=oArp;jRHOuj8HTsq^LbotD$(V0E z0)zd9bq)E*tD#uoILw;9YS~Z_H4;!Hb|`G<;N;WtD9n*2OhGCy? zgO?&b3b7GJd_I1ZSv15MydzH++`_}&W>zg+EW!1Y8%FzH>6|b&seumnegz$1VfDm2wV0y%QdsBk;ZuOy!-6WI={7NKE_~PFK+KlqJ|r#PFg$Xl&HO&99sN zf>8i^@W1g&cQ^ZRy6K2pts%zQHn5e~BjH1c|1B2LprGY7Ya2ob1zWS3buYMWA6Y}&TPM#4nTIAQoApAzye{ppH+Z^3suTp=wM__ zDkS#NT*mFPARaz17y6T%Xs*Hy^986R(e(s|pkxeJp}GW0xWO*u#czVX*n#h8xlpQ^ z_(E<@@+r_`1gvxVW|Z_Pp-|-!`YuBB&bWt(`BFatwq3gjPWY~BfgDj2Y&xR}VdRx7 z8F3uBNnYEjEO?m75DV|nlf7TMhc#1}(Cn#4M=^}KILrtoABVj=?2A5T9GO83hzajb z_>88{ZR(dI!Im1vj~}S=Kz+^Rr|!W4xKh-@9L92-$EdMb;#BTt3GaP&9~bE7M~E+s z*qB#KxTZl>1bkpb4}{W#wDG5p>n}6h{`djC#m&bXJfwriarrXA3k@ywLz??yJ(hkW z8|n8G+`360htHnp4?OddKc4)Et4*2iM=Tf8*V7;WX4rmy@;A%({OD)Hcy##FbHn-k z*)y|tTWx;)Xs#`c)Nm8m4H3Z=joj*Hb@66}!oC}lA z3Kl97OE8J#huQ4lr&%KN)3^=`vAGV#ZJ@I*?> zLbSjv#Fv?9di3PSpS&`M&kvuN)vM@x`1J5+uh0`{`0OX2t-~Mx_R|xyA|&h6Cx3f- z^kZHO6ZZ4b&p$u^v5@!tr=Na0EUkR>?Ag<&rLBK@@+0`Y$R|fn51&AQ7kc>o=ch-{ zo|e`>dj9iIKmM&MgP)FmdivyNa}5xk37fA;KR`_40g?&}{SkT6Hzzm}g(yMLHgF6@ zJ0uW$fb=n9JoEIW!S%5w(b+9rSvlcKpK_o4c=#>>d>@h*x*ncO+{5#JhKFZkdwAZ- z@bFwOJUkaq>VbaEx+K^+FMl9DTFRB2Se&|7Ju>KnCmZtM$-UJeqzT+4w|9vQIlwLQ z{BA_1HLpfwpno%>fVU(c9k(?xuSH}jILgF=C07RADY?eFIMkF0oM~|OnJ+&P;4fGq zUd?>b@%+~M@$2^Uw?2R$~nx(Ylgw;Ac=Lg zmmfu(*`?A&0Mlqf#is^p$~Qoip?(m=Aum)M;@6vC3J(v21~YXmZPrl^KU1FC zB>RZzND(ocXA9x4HMY18_ABfQPu3m$4%rl-yEN*;KY+^w{PCv$>pF+yYvGM8W}DHK zxZ81kCy%CD<}_O9qGJ_GFoD_gBw=P+C);9?Iv;H((&C|~vf9~xF!wjO%HZd*5#UIS z7AZLrqBoGbev`*}2S#(S_Xwrt>rP|rQe=LexpkJ1`wuuGx(-hEFrp1)4w3W$sc&~l zyWBrQxz=R7l|3`t;ZEZJ9!&p?J2L~hzx6`}X0g{$y^tkytBT`lw)u=OnrLekO0Lmm z@bCHg%K{Uvan8BN``KI@T_x`lnK)h}vKaj&I%qVUcTO~lPH#BWDEJ@nk(w$wKElTB z)e3>A>Tunxz@|;sNR%0(Np3K;s&OH5T@r|StiMHb!(E2#w7qzMCHv9ggTL3JZh0C} zgXO|-4OXwpKod~7 zMo!teyUx6(;hr9Dy48sL8U4^!8X`6+^ZSz@|A67Sp^;&SHRz}YJ&ho{Gy3p=QM^Oc zuogl${F)d>`)wioK~zCZWx{$lp<-@GgFcH3{(u{=Sr z02Wu+K+FXhQRc8GBZC)6qm%=tW!H$Ra}h8-nHYkJW}r)?ts$PJMIo9Hfei)Ancx(e z+P$SMOu>yDC|=-Q9+4LoaKYQdR`n3H`W7jT~6RI=*yNpZNo>Vnn*G2*>xL< z2XgW1^}3DR;x@tf(<`1hw+ZC98QTN1zN82L!s{MQ)vo8Gn*pw~b)ZAG#Jk{cZ3%MX ziHihH@gwZbHxX9w&$~%}+D-C&H_5ZzBu{sf9JNUn@PO8L;F=j0G?(qa>eZxfBHmzc zCjpmi@p^9Zs(-ewt=jBLK}C2r)FWi9F5W#9h}9r&_3!YW)e`Tu^^hiy@g5pDJ*uQW zLFc=3PcQVg2yrp!(cl$b5SsYS@)8r@Hcl;iYL3NipP*O1;EFEkUpnY{$ zYw5o45c=Q+{1voGwliu7ciBW#8+11a5rxgwrox};ntIBCvwO_a1Qz{mUWqzehobL; z3tCeZCBY$nTT?}kq|SELSEV^!V^Q1|Vu!IOyGah)B-#^*FJ7AffnZtPF~6q2UYjNe zq5n~Lz0894rQ5enW8wRh5NewC<{|D#e!7 zJJ@Au`wxxMv-cEflDUge_2lKcJ&L}cOhvsJ%c0SZV(6t(LBEXF-Mv@JUoVvkI%R*- zGto`5_9xsi%w&lj)iEd*JG(thpE|~F+8}g|W`Yq+GP9!;PQQb8$7G~?osf*N2T5nA zt$BMly6?_@N@qW+J?d8f3@WA9Gl{Y^ebZD+*Eyj|M7t^WnD7Q~=#FVwN~@nti$z}Y z?(cT@A<;d&rks$DY_IN71F)3AK5RFkiu-^Q#XUxhJ;{Qs4F+4;uW#l#%t86mX{<<-|NJY$9=x1=#`Z=2ev8#6 zW~uZfTLZy(?C@zEt>}2g-{_5`mv=xV0BYSN3be$BD+el8ioHvk8Y2kQ4@(+k1}Z0( z4h=zZx`ueQ+0T>^_`-V-XJ{oTiL$yRX&x8_T4_=-0hZ=rPv?I_Uk?Q*$Bl!MPpt0SLw7%dQc4MX;#MwpcF*!y&KXaFm? zs^cm$j-hX+2JEb;d5Z?ruV}lFZ#A1S0xed2im?oAG(Aqq7UJuE4Lak!SiF};|9Me& zK1rCwhrq@msmvz9j)miG#Lm`dlL`7;rA45`knBC&aD@^rRexZeqCei|)W*wbl4wKp z)HfH?$rc@}uyZ^>bWQUSDTzrkg^Y>gr5A=Cc)Z;ostp zQq4;U4n?_*tsRZvoVT|lZ@$dizBSQ_d?PpXip zadep4U?gOkJn@N!__o-LzIs`zRw6lP915Q+j9~$!CQGyr4=`sJX>pnr6Z+Tf5=ZLs z>ra01C7A@VoCxJgZ<$DZg>vv3o^F<-jGiIT|6c`#D9wYVL?#fn*(mlHL6*}89E&i< z(Gl+KUlPz1o^H@33=DmjKG1k?H$x7?2{gy+F&=_w7pPmO>$h;Wx}--!RKq0VISk^0 zj!%gLVRg3iiNHQ&E0yFuD0!v6k-mt;L$X*s-pqg_kI73FGP)XkX55Y~w+Yzr|#Di=m3KX(Q|P z-R4#LfY*Qe4a95v@xp%mTl!di+cN3bv#-;M$=2O={^A?D|3mykD1?|AUdx)TXP7Fy z_$G6+X9;8F7^8)pXiriSSD8VtL?5*m;`U~#3sXQ1_#-`;{;j6*dBC6yLqk=QhnTe| zKQezw0>B&G=%~oGfL+U4qgTWwolq~7z%?}K>1&7+qLy*pqkA)fmE|#O$pH7iW^nV> zHB?GghZE^#Caz<#2o4^CtOp1gpkf}B=arlSGfY+PxEK-q4LYaW*_a+kDfz3lEaGru zAj&uyEneW9SbDky{IHO3?78MaW4pLDeS4rt9C%mJ^_(~sHq;o*HgKuTL(1v;91c5` zUZ@6X{TvmHJ$1(X{*!gv!Am{x3Yt6`$c-&J002UdZSipABiib!7cff8`5GL=9PGsk z9^K>pnFzr25;y8ble5MAoAX_0++##;)pJbJzIy=A z;NM{&Jqvm8pMU@3KoD3Ht-k%!1Nn^uOBi_~v3B$0>_K;NAO=kG!vS}kzE(69u*%2k|F8z+D-Eip(@x%(2Mtae@ zbY3}wV=(B z4$E6Zem7go-S<%ZQ3D+AZZJTmY5{o2C5(XuOHpgcrmg03C2Yyk3Y|mJknhsQ0#NW) z!qZvA8G;=?8oXRaLjA^lYQt<{VgQVCnxhKyBw~17|Iy$aC-i|-0dH_}=>Tx}&q+z& z3?t+YP`T25Z#HGh1t4=3KwCujpmsq6xVt#O{6p0z`sGw!IfP8SVTP+S;odERjaa1m zEVcQT_nmA!GVfx##3*cW7}I8pQjN6+B|$ETeKk+klbi((s{p(9bLP62KBp}PNu?I9 zH9s0$uqsCF5)^f zPO((8hxETyxt???mD1q4TYU~x9R*Sh3wv3o;OSAir0mhTAQhE;&Eo7ZPc&^+i8B=^ z!;=nVDDkjpxXA8J>}dz=ZP2E;o2>4mUHAeX8hZgvttsv^4NljH_k_*6u<lL0Aan z56r&f2acQKPX@vr&$m1ddW$%Yi-g!L)6dK02XSGIJaCH1Ki|sUQ~Vx^i`MHU{L&%_ zb6w2)WW=aIPZE{%17dy5+#k=d`W?vriU)llA_X>hcrdz}4X1c-7~Z4|@e;h`&;><= zT-yoTCJ~FxIc(O(uS^Re7vs;kcoyO&7!TSA%EXAMQ;5wtbsduJhxbWZiAE*JNX#ma zuV#!_W9YHY{8eu7Ab+xWHMpF=={=)J%MoD0!)T24dNIu=0q7FX&u72{LC}RuPdj1` z1vHSe#0c_~VeS%-1m7_wnQ9NR_;>#H4MjO9Upn?%Gg zMe0K!$z-@V`U&iW*I2QQ%|KC64sY&7S!e=4Wqs2r|5y*gBTw`ej^COr`VUNzxs#Ri z#&S*9K|_9%)S~1Pd=dPJqxggnFM$dwyDb&e)u<%lxM;d^(*I~+yr3#USS(7OqK{TZ zfI{%*NvUCPB6xLVCbT16wX=!fz_URTl&V+*oI{etu`cbILRpsxmGt79__SnIg1qPq zQX$&pnA3`=E;0qz=lGe3)FZhKe(?=XTBaR(rmozH zL`AyhlDOp=xzb{`B_~RhRfm43x!qgp4lw<5sw_5v>?&3>_*&#<9?V}is-j|5wWq+> zjjSN3t64$NtW{{!*r*aSwfX}^U*cv6*Pzh|8TuB1E)iNTBKWvxbkzy1v=3L;kcz!o z6FR8+lVa-#tK6tAo3!b+Q&qqnBusfC6~=1UvO-L*3$23=b-nnE`^IJEG(PhcPUjRn z_ctjfMKFJ5a0~ABx6$0b6Q_YF*m%rGP^dY!k%zisLE&tY)paqQN42o!&6olqN0elF zu>Df{)<;t3Cr>*kAz?7GavX8tR38OW<@JkisCwwIk}&BKZRg5sMv;Y6%;@5p2T2Dl zDg>!bh0xC?O))FF1|w8UN#odR^X5cfk`BhxDczfs^A*nXql)6VVN#soq>fHFX`0NV zI{`$Fna)O}#Y)|ab8I?);;1T%mX>H_huKUV&k~_37A_;jWPBxe1z4cqA(TXg5zUq@ ztbz{a>{<+lkT}5_4*aYQMzG%&_0t5#_#!nh0i4vMHu$g&&h2)k@dQ+sA3#_M(K*Py ztf-p=W;JFG-$kv7cjF=#E6m%`x1||cGN|O8OjrPJ-juJEM2T9w&@kn%b+;r^f9CBB z9TBt#3qce(<&@V>g5}L^kvLhDeW@tN;A5YGx6-){8YxsS>=xzL;rmx5kcd7AzUNi~ zMtUKSA@bIZbWnKJ1RiH%E_qE5P~HgfYBKu?WKxfLPq-)p5mUu8(Tl;FKZ}vwNVm|u z#(rb9V53`&EN6}m%e9=)`_-JG_6R0@p@o-}y^|%y1s))pVK_-(k`~hr)V%{_5=kTenwbZjveXW~?Tcwr_!&y1>fWLydy_MoU;R^>lp9{A(DY z;6nylOHDt)RY4#FkEe?VzSM z=a!lTYohb%qN7xCF4M{^z?hHc!Bx&qPoepvft{zTbZA7BMHqUaDf9^(9%Xpm+rj7I z!7YPf;Qefj`-m3;@0>grFiaN`e%MQ7>}Z%Px{uZ70pP2^;*#q zH)~PZ;^_f=qiV5HcZgiW)F5@w4Tu{$5?yOcXFU~f!-mw-!bwcah^(GfQwPwl zMw5UOQ;8C=qgg>l!~B4@O8^MWR_weuB+wX|q|44zBPCo5H=BM-3=gpC0tK{Drko)) zhGq1BjSPKQtVh?=#@MWgGP9J8Fl+10yAeai#-^%S;JXF|h>>**5c&&Ly7sVTBefs< z{a1SYCQ00V^AZR_Nz%v1ZF#x=mp4PA%J|I(7hQiniuX@FI4l7RV%W0-As!uWNvQVm zJC>`vt>T-;`0xQAVI1(9vX)OdFdpFIx&&3Ha9YL2Yk5MUS*))?apUjQHqS| zHJBYI2Kxp=1Sa|LCe!`T9Lg}XI=RWfYh@!mcWQUH9HQFbLV?8%vq!BRvxf)V$!?@3 z%}|cbbMb{0+M|IRx-!(AMkKIXq&TSWmJG@-fGnb{@g5gqt0t3$OrZPmO~-QC7FxiI z39!Xt3^Rq6({XLvFyx*q1KndA28@eI4cT>1nOTY>B!lR%h+D`ca^LADuwT-6aJi~! zV(r4FByh{O7Sb(mG6C15wUykknuz^Uz{g0Hi$D%cvC_#1zjVmL%C0DK2)vm8C$4G97hgO@k0# zEo4LC2Aq7=%?a3My#u_v>H*nlWZ;S^gw1oi#K6;TC>_Ixfb)_eC*T(g30|%jETUcf z+}^B>)z-eTXh;dP*9}&M~W@tc7OS?6j;nAsHS8^X}H-fb}{47h(;TrYH;_E8quw|{X$YLJi zP@Rquma19p96??4EV{0I7F*Xpi)~gxN6=9V9gPcuk}IO6@paK*FnKo()}nlkw3xb5 zT5PLcT708wT69r6Evl-5t<8V4%fCT4r@^P~&VycZtZ-bRbE!si)^`=H#h4kDVCWh^5eqxEghZS%R1y|(wiY#zLZ(H48#vMOfyr9Q zRZaMp>m!4#*?4s@ePcJK{tT5U@?a8+{c3r-Hu2F@XS(5=e7IA*nO;nBNk#7eX)?I% zU1J{&HYm~+{aSF@Gl{bGHYORlPjQPkXg=iKqs)~lL|fkY(WDCmuZ}1}?uQ5LJ6zJn zTZ;*|P;CR4*JY+FUJ~PhVB}xS?_{O{2ZRW+BE=5J8|)ZHY!I3d5U9O)Bv@@mr9^J) z$^9SInQJdu6R=K)x*EA39$exP7&;U2YT^u6utP>I;yvDWNr-cKvnGdnu?-FTq>XocZV*d()nTLTNQ**2?I$$SJ*NhqdZhdn*fsR1n5I_Uyx-_5n@-6zO3Du!B|d(IVyz9{;Gt14eOQYYM^D{8;Ej9+UJAKH$PJ^|z1QUeMR+$g3CJ8Qva(`a&lmzKwdK$@bGC=K z+s(vgV0f>g(E5 zK8Ln#?Cy@ySVFesWGQ~2n;a@U;XA%_V_GGi_iP*XnVcv#_S|V0$jhFRM25CBu_>{> zX+J!W9x#Qb2{}q&(}gBj-HT>a(UErMoX@EZ7{G0~nVj5i7kKc65Y+J1X3bfw+~ksQ zLxPy818Kgzzm|2&ACV0c_R$yr3H%@-qrOegRRhj<0!aFCZCVf&66PMP82otEgu>yx9SqrWM;!O|?_ z&0bhr=S9l~@0Z(H?bRfl*=_KOvSbaOB1}0nghZe|Y(l|7gCt6Se2u*oIpGuJ%`*R% zS0!Utb;c(qg=EZCCEk~m4;j4jSSBk;LB!I+Ts#%&B@HGCDzkjQm|icjT_Q{Zh`S*f zqNk$y`&8W*GXRaq2tcd$04#uP0{=H1cQUiIIP?87HpNS->l&v(PKRd%8ji9b`DM9v z3E;6$rvMC+`a}^)jZ(5Nx9G{R9pf4~Isx{knrNcP9ahMH!n~V}5xP~Gk zICft|7;!r6Cl&em4zHrl3KA);0+z=*N$eeZ-`mj~s~|3wB)!Q(t=5}1 z<8?%RSi`|s_&0y0VMczw=fX8(>@cMGIC#s~#THZkK)u56%T(!VeY0dP6u28JvxBfU z7a`bT#2V)G?9l)b?S78tGO(v5Ojazy%NPpL6kJ(e{yR!^RMTTutxOBcQH5P`dq}su zg$57Pnj*lS`sDJj+2VRh`EXM^Tm5^Sz<68&qtJuO_;sj>RHSyK&B#m~bCursd~DaAcRHyC>)n&mZ@W0H+*qzJc3a(%)iLT8hzVM&vh6e6^ocF7ze%q;RW z<@Z0fIA{1d%*6X?T!Q6{z9GnrIb8{2B0lTFwPscOrn0u{3!jKXwKKdwi8V>=WYc(L z0J-x@K+dML_!CGxgYA?*V(iQ(!pu}6fx>}OXg`-|>)(!#Hb zB4x+Ok!LoM(9MPtu$b7J4nL8YIb4L9_FzGItrej?gJI`577%$*O2W+aEWx-6^W~W9 zy@#6-D018RU4oeoF2UlgCIb8b7oll}k^q=ScqMdVfhk`LoaP&rKTj_r)Xj7wFwd1O zq^fIn;L{39h@Bk4r-n97ey*W5heS(ooD{;xCe8{U9%P`Ids=Wk>Gjxh-s`cPq=yNu zMdWgCAYI{S{oFj-3bVtUg_i~I#_F_^kEiisyT4>LTY(UE6md4wPSfvC{%BBJs50C$ zTDrt=`$2xB2w%aF_!Ty_sC=#V?-2^w}99&gdR zF-obPIJD7}*U2k8^qpAxP|!f?gWDGWT8<&wIAk5H6S^Wqt2oX71(ur5f5n|>z8jHf z?(u51_-{1Q797vCyKpG$myZT=GJr7>J{U1&;he5km^R?0gPQ`OY@B^IxLkTt&W8ni zO<~@tiz(x9GbA3C6qRm?Ce$&`sW< zj6Lly)nrkwHZ%VLitx`r>2}Gf0 zDOYLZfS)C5JeY!6z>!C8lVGDR@USYUb1CcX&)VUW`WVu z^s;YZLNp*f|170uF6?t+RZ-dmduX^&yFo=PBr>jPOz;arU{+{mXE@!&D&2*@K#q6O z{Kmt}4kGL_L=1z<6v-6Wp6Zhh5lah6KEerO4}UmAZE-7VCvQjHW%~KdwJCoSHYNL# za5QBz*0KiVWY`QFX=v%e z1x-eiDhb+;Ts_%4Uon62oJ7A$HKG`9VRfcKWKTL&gCu%b+?Rq->?z5-eNJ1!3(@LI zNzZpkdIvX`E^UC@&E20Nh}b7Tfvh__k)TH(=}D`+MkQ3QoxDQO9{beisVSTOPko{jzLX57 zFd3M*4#k;&OE~?;I^aD?GVZaINt0JF45;6a?U9d(K<79TaQ)of%7I;wP5y~9*ck#S z$L^)ujulT+vbHEx6L`=y&7!CtUK#|u8F0u;N*PeA3^Te%tX?=MR(9+kh-mVCVR!kt z$ixsH51b|?-3YiQz-t!inv&H|uHaD1VHy-%;7*MJJk+Pu;Ar|xby8@InpR25ludNz#&?HLw7ud)_{_}&SzM)H89%n zWiTjvGi*Anw{REmhO`V#jorRylL9w!R;#=d7mppvbPA&EdAf@*?3U*(a@VoHewj}# zyrfVRG~aT~1&XYAIe>7yszG!rUYBt0wSzPaJNzko(is#?QUDG~X?OeZ88ujC4WJ0d zKKNS9;UxzW{g&N#d?A$la)aG=I6>fxY^RIO+gF+J6po7-li|2U5CozZWhvK^`xqK0GnAy3@wiZhXHK1w;^hlUBbh@1~)hMAc5@dIsNcBvNkIgStF- z8L1Zy%zCoM8};EHt9hN;5rF;Uu$zuzx$P^ekI#wQ+8l>JA^IyyI=;yM_4aKVj! zv7ot_^3{p_tH^{N?iJ|6RAn#jkb+|M;t_wNlhJ3Evn4X};Q^=Rj(v1NjQlZG56w*y7Ng6Brbt5c&$#}CA29yV^v?uI6XY#p{KmtVIC37Q>k0sI zc-s(4JP(m^a}vLJI?KO^?Jhgdc(g3;KYJ)^T0tl^Okr9uE7R4*%A&*S<&qSn-QbBk zUMr6`#0g^uQD%ozHtZB)ycRt)0e4nN#7e3_$TgN*a)IRKGK_$%9>C?+oXBH`>i`{e z0+tcFraqz)Qk%obAcxI>z2%Msk=JmDIdC2MLK>7%IKk$9d6 zC^ml;IGnyoh8jXmKit6P3e`Z$)T<|} zvCHdXjT5d0UXOE>pnmBu74T^a-i>Y9P$e(g|)UW2`u}hekA4M>q;Wk&dz-p4!QC3L1B5fp%%Q!k} zbcxA>O4ViLS!6>n3=}4r4J2@K6FSb1yAtz z=jv^i!rt931)V2g9hR_ExH6?KNr-fTI6eccxz|4s`0yaCdzQF4C+pK199E_U`MQH1 zaK`|);I4JfO$s<>sUU&hW4flFaIZwDoAMqPgnsc~w@v4Bb;s4;6WWKTWGeWTb(M`1CJs#1$UQJ`(`1#vxf^~y3@f+R; zW5m*e<6(x$DldO(GcCSN@x8vMYS$uQXnw2R6s|OeM1CrCu4!YHpiygcO1M$Jz!XJR zTp4;bn!s7|Dk$s>XbO^<1CNCF%)I#Zc8U`P*F9*rP6)!h6_@ zePXcpEPTF{n)McthWsHAc+PDi&1SiLJ6a(K5piaNt(o3zzuK&C)+Jf%-cuy8ml5KsLTOGog$|IO&Y08of5Q<(acvWjPxQ^mt;QJ zf(AzGVXkJKq-*uCGH(z_&N~tpz1woJp>|s-i--L}S5lTTFdqOzuhoIUYY7cZ1w-Ah zVm*|6xY?uvHDZ51{6h$?5j^6-fBrq|c7s{Q z)%FHcEX=F%STrK#pW=b2<>KY^dgQ8KgK{;!VPqWRg3us(&&|E0U4uY-s+|;JH>Hmb z4+}2GIB7%MHZ43R8Lk7D0bybcUhrPwb8R3|Y>?kDH8%DP6J+25h%0l5? z_BB{j&!bGK(yiiM#W(hFT!j{{lbE)NtNeZDvso}p(fzi$ZjoUi*2SA`n|;)QF0m zAL&v*TQNvg&Ul+#>|xqGxC$OS?}`9NQtYaGC_-n>H53TBU;AP^o1-bRuOX;~;Tp5S zz;OEwum-4UAmmY|23#JRXux?AadJDw_})ND}wpgXl=pR7-`cNI!B%lixq@*AW2kY$Mba{Wizl=Lu`j&t~I7C)Qjo;98(6^ z<%;zuamWi$s75?%V0DUgiQwTu1aVhWgcuxnaA3(hmDp`D@N~`}Qw?KwR1`BeESTxO z5Fu3L&2)j7RpXdo!eLG?Gf;_SDtXQWQL0c~B$}@zOVd*!I+zs%4-YEk>wH%vBCwEz zg}6dAwWI=`-fmdDWnNYGHE;GJ$2@#WLheS{c3lEnNt9gLmrhqBfyl%>5iSFVTiG>QEovro*&IpCHmSQ&-H~ zj^;N5t|g%B1zcDohaASa1}PBXcQnU~Z;6R{k^M6C+zE&ZydKYH0O9B!SDD~Khwzy} zRn(;+526!5zTT%#+04o0`YEx(q6E3_9X}A^KCz;3B!Xo~>2mo2?Fb&M>09Co=5Kfv zy`5gmOWX;_%J|c*(;=D{{Cq zY`#^{_Z*K}{LLgOxS66(n0ip1;)tm0)(Qz(JLOKhrK_uHaZFJ{9Z}-%&}-|b$TTX; zH-`a5yFoxf%^^ThX#mhq_8_@?%`oXS^JnU)6*NoTw0`@(HnRl{(Y8%LtLOyN{xkz~ zAM0T+a@C^J(!Y9&*gGmT4t>UODYKxPS*@IZK6wsBtSzZc&<>x2YTJ5SpeRTxCyFp1X20If=UQb8a@(I5 zC`t-la|0vC7G*BRaCpSA5JIrta6dS9_i#7HIY>!N`OF=t8uDXTU!YQJD&Svuq3y-? zI>{13tixPoh(KQ%BJ}lqizAAtOoW>0Nl3H8AYHyCRB|fV6n12kzZJPaU2!{lS9}gr zm@49EU!9Qzy@%t`0&8~I1V2*p2|Aj59`V9w%tk_>pUQQdP?mixwaE$(>0H3W=EE%i3oA$MM*TWaVY>d(-+61IR-mC%=w2i3$#z9o z=tQVT5~uXk)7(5fpuaz!F=mcV5eGOT#sz2i{%|VZzVLG1;wHvg{f+4K%+G@NO`^T= zwET>;D;V$uKIRLa1tusXe+HEjOb#y<$jOBs??f?%VbCHh>_T#8{4^^fTmoxLS8^5Z z$qL!Jw^VBHpiPcKA7u!sUd#3OnAs2NKcP7BTMFJw1wqiEMG-1Sp$KY2B|3UKMcIkQ zN{>WoZ#hy6QC^)zFjfCyv>M;eK2IOxSplr-^pzsGk)j?*Y&eU)m1Y)E+U51&)g8Hq zAi5m)kkAXf2c3raK^oZyoa7Gi-)$cwlAiYALtkf;)}VQDhrNNh9eG``tHx+867@KQ z`m|gR57eH&{)GLX+fTSe=oCuaV>rej7NTsT*F-Ag!|?L>;t`s~&1_sqaRk^o5O<`O z9x7sktDN(yT4;v~Ct({v3B(Evk2T9kk`Rzpl@V-k#`95NZ>i5s;Uo`8F+1>QU~WhGoHFC`W50P&M;tE zL2LC2U_`|Mu3dyk(w1T-F{`>IN^4D%G4QZZ z(UKv;MymYW&h`SevWb--b1qZ8P6}<5PJ)7t`LHiKiLb4ue79ZeWz@1QSl$_K^WbVQ zJb60BVfE2O7@bnML^r)*Z%Pa&zPl-8wSnyon}QQExWE0jo?#>5VtR+86e#6r zvFn}s@Xl0A5bHzpnz{G z&wrihg5+$x!Tym=JC1l&e;N_E%Fk^h%Oit1M#%;WGrRX6-a6`ByJ>6Fhdps*vT>B= z<2E^0dGp-dl4z+rd3h?+w48Q>afTj)8e5sMx6U-JS$LDm7jI3{IZ)=bI{RfqA{|PR z=`vsCvY_fvzQ5`1;>gV~bXPaa|BcQscn#__X}WswcWtCV$~jww4$Pd?V^eVb7&i(K zc!sYEk8ypAJ85VdU%1P}2q8ZdbfKLVPS(R+!nSgX&^Es8bki8ns zg_ONyA#63}B4PSshHbS1g(7#!@P!hb!^0PF57H65$8S_Yq)Ml2&I+p(u5jyF^yB-i zc<#F*C{>7gt0}L(y+-()PC2wS@5onaBo)M6s8GNn@r-#VR=n~mEEQ@?4VaclLAB(a zELW>7-&4i<&~7gj$({yp_#To}vZ)G6$5jZT|8fNJvKE(d5|FesLrKVvtU+U|JbO{y z7>z09rr{FRm=ZgPq)*nm+DO;Q3-zfhA67?cmM?g~QzvifMm)%tp}axp62}hMY<;^= z+9GogJKkbrwB52?4}*z=DA?*`%F@FMchppIv3zy*dRG?d0Ep*NMh!Y^GqJ561N0kL z2zf1_A<)_OZn^5{#e6Mh1sHz2+bIq{JhVUdMhXStu{NFjqO~owpHoH_F33u=>fu05 zWUbO_g?WFw;7y2^5K*Syp=6ll7m`}Z#(q}D5Z%W}Lr9w76msCe#G!`HfTcckIZHh~ zT{sHX0$=xwj-b^$l0XN54)GD$maW*f;Hu+|JFitHT;Ch1FrB=8JYJ2D4hOU86#J~G zEC$BuS~?9nef8>^od`LC1(; z!L;*&hGRyUR2!{!Bugfp{QodrU`7G+J;syGcoYyvRGZCw`f4$mjTQs?p5rEw*IxV2 zoqSK87kqi`!49btS&WpDT`_u25y#=rpTdf6dpstgfB<(N68_#bOLu$rE>%n1ry2hNEEzQK&$=Q>a%UY-ibL^o>W(|CAr zx{!r_V(2B-4B(>S1_^H$clxkrrQbT9c&nd>Pf{<}#&e@4POd}SIwZLYj2J>HRACa7 zq-2Bp`aGfWi4?^0;B-F)vLh!wJE?xV<#};08B8Ka`7#seCq=<*cS>WlM#e+xO;jXF z@PbJ}h$CmjCByuvmw&{K;C_!9`KRcMgm7O}M_@OB$wPdKa;mQfw#l7TzZ<^ePk(JMBrUmgpkhF7)B_Ue!V- zx@6#P#nyDh(ujgwuG-c`M|27w5JcI$LE|oRiRA@f_iaPNOr})y1H!=G-($;SI@#F= z8QNc2=X?90pv-jjT~r|eiq{ewMXzLhmmaOi9^%br9FIy{HfR$yO@gBkdI(Df`;!Vy zp+zZ`QFUGwLO5>i#56U78Kj6LyQNql(Tz@y5!Jgb9R!$HU}LY1#(6GJg(Wj>ieOn3 zXj6g0#8U=>EwYaOewC|5Qwqm{9VC>(_cwQtT_KiDTT`5-YlwhWm>7u-GNo>u6H8m1 zEjnzr?ZjN;E#Sz619U$(6``2bBZ0Ppvo(rgHfi>cNOHPj57%bh7R|Y7WTc(zL0i2$ zgmhKjJ@L&NTS*ZrGiwx6pk`tuY3FAuO0XN$O&0l1CBv0pG(=m^21Qd-whkk#thgLk zd+h;!IfZTK;(ikwO#l2A=S41b~2$}T_RqqwaFV% zzM!^V&Jmf_3%ODOvQON`$gV^i^%jQ;g*+-1?u&UIyqbu`1_H>;rnq?IadJ@B&Y;R_ zT6R1_$PXF+nhA!OA(-1tTeBNZdK&8j<@)9F-P>1~O2X;KYpUoI9H@+{X`REhHRj!9 z=_LxW4D5~T9bbD!T^mTs>%>-|Yl1@RH3CMpF*q)Ys|Y-QRj?W4J)DQn>5bGL!?Tda zxWR+k=?ld5Sjg~nwvrLLNNctp%2hR=qk^^I$ylx0ZOJ87O9PLe(pTI~?lR4)ljz_$NCF%AUK_;KP%{qn42O)3V1A&Sru?wgfQbZ=+xV zZj!%h5x*MZAg_sN%D8NiUY1iD$pI5OUC=A67RQ7 z$qe(wGKA5Mw^=$RiS@s;p2#2Ndl^T=XDJrT*B9_V+(H+x^)@0P$Mn*|>Dz9ob3S zSC?D`HM;tT2RiG8D>tXmjUA+jKW*+WMQpRfES4zgu%a+P7g@N+;`3b)>w>+PgqM7K z3E->rrJurWOv@a)P@}mGCW*%FNZEq#Rf80AYP=U!*-sS;QJCDe*(t0slWNG?r+oFK zvNyqf#GrRyAnIZ{e=C$Scg}iKG&jn6>GT|aH_7f25=pzQDrEcELkO`G=yhW)lm=a% zhlJ{x@Le0+VfmoJZoF(BiOS-H$ZDNnu#E*qVZzuy)@e0~Y%RWWOXr%X zu_~>Js>O3nvTiKoN!8K&R!g}|sFDy!&Km|9*5Gj=M&Cab`1y1}{DZ&aG*9;Mady&p zUOm)_ei}MG;^CYflJY}QQJP%nKiPc6Wv(>?5t8RR_D^S!dov~B792@^ zIUB(xBDY#sKDMP>`4;X)eGUz^Wxj}O7$Ty4^>}y?;YGcP;G)VzaHuV-Z}*#}1SgaK z|sdK7ol3?od zuc90|U%Ncf2x^tG0J91{|8acB%8Oko5E-wm2k(mwlyTbNRutXM0918khrr%I$ z#XHXx5lZ#V$vY052wFX!;?@9;B&3Ma^g4LkWp}IKYvq%2w;Eg$H{-jP%PoAk3dI4( z8E^qa9LVt0RhD z7Ifu9lMxeI)(Q5|HaPd;tVPaE5S}{lHYnKIUi}#fQqf6H@31M_Om!>nsJ@YSR;6fj zk9nsAvoz+XO@U2R1TB!eCyjP3x{bEDDw!5=UP}-}xvA-1EqRdq7`IfgKEXQ>qt!$d zSawjtZSoxTW;RCUeRcBk7c?_*G6^Xaa!C!9F`G~1i904Rk|25b|3vyU)O_B!XAAHY z(EfPcs)i~cqRVRNv9=l#)2@S_$JQlc&m%6+qkz!-+Xdbx_LV6E%*O6e*!FKrI0Iu|Q*4X*atpD?|;OlyVB=t7UCvObaH} zI+YHxy_t>?32nqDuFek+pYnn?wjmx3&bW!vLw*lp46wV%3aJ|Mu1{{k(C)5!^gdnx zJMJR)s$L|4Tzn7tRkH)N8qqZH;DytpI#(k=R6Pl^47Ebvo$cZF>U%O_W)1sP23$?} z9_hnBx7f`3E{-WZPkT9N)m?g!<^&JL{Y3Ev-FlL2m$5|D4*QdaM-L#O|(KQQO^=Mb*`6->U;Sj(+}*eg?$*^)puLUj2+^ z*ej=&VZVOHa_rI1Sn{jg{S3&zx1X^oK%wu>_8$F=O=4HmDJsLR_Fa(g*Uwmew3+nQ z&wJ4C(t|Yn_A?;eLr+TFWh~O{vOj6Qr=J1&{{0No->;t$vAZi*)OI&zQS$rsGtUm6 z>_49Y@qYb`mAY3yV;T0!sb$!&pRpW!^fQ+HYIi>a^6%|uYzq7KGmdA+c#8U9$#*rK zf_zu|F39(2za_^W^BL&BZyy5ET`J#$W)D3{w#!%|YKQ$v^F93x$oKDOn3etd84oC z%#(-E3(J*WyWhurngWBO_N7AYhkr84PIEUxsy6X(K$u9N%PvA`tTIe)jxqHMzj+q`2|fLFCNbql%-Otr10-iFd8ZrKYFS+ubq~>3%ibDYIX* zODXi$BPUootG(eO&wadZ0{VrJ|qZ+ERwiO;$uo zo!w?8?Qf5m(13K`35}7u&x9tE?>(W(Ec`FF7+Ly$n0x^h0y&-Me1HHuMq#8JpHXT z%ih^qGwIIO3etUBtC6}-Yt7_)x7N(EXKMvRZ)*+sovl^cK=iTh9QLz%K1cnGG8*=i zxDn^OWQX-v%3yywT&}(7CxI#;3R@ygRb-{Gy*j0X4LsI z2;m1`z02i`83M_+EgvXBak*6TKFU1>?kVlv@-v*BI(HnX8m`ptB0(MPQ@MNlP*!cb zCi_wVy?Y0l;#igseafIrcBl)A9U4pZL2st18Mh9sQ>D^8))RKgo)Bx7whtx6X4jG= zaD^R)?PfL~yc>PHk~o{}wX2M_$vT?Ebn*p0TxfW_vk=!CIqFLwKx z#<-RA?L6MgsMPCcZjBH995|7}dn}E^`+l~Loz;G(jt%Yx)Ae+JT4Qi-M9uE!^+rH5 ziB=VD5E}XECL65c_sAwjCQ&fHh6%3Cu7Has$WG`-0WSOx~vA% z-FE&Aca6TwZs*G{hlkG)?&15`IB&Wb;X3?2eAnCse&f9$`|>fGaEREinIO;m5w34% z*Bg1m_QkjTtQ0$%@6g=TEZS$Zn;?*U49F9-x}>&c8zuiJrSKegyB^P zm*O&QgLZvDiyi(~$+4_~-<5pIXO9kv#t*=a)RfYR!V6)>cQ5dKmBCtc!;;$F3TaVD zp{h%@ll}0(3F6jUGF>0xeWJQtrp)y*P;#sgeRNdb;b+d3Qu3xM_v}VCIvi8oTi&Am z9LAX;7?;yJF@6qhalpfgpZ|C?sIu_zppU%AGJS0Olt3SUUs3n*b=RcKbx`)uL`}zo zwAHc8O8W@d%WnG!`aVnUBXK|5?jz~H%i7HzA{W@ra)2IPg)41+eRSa7QJM)X}t64>aLOUkT0I#zGIy= ziDi_*xRB{Bvs&B?m)q4C!O)>hQ7{Hs*^ZGC@r)iS0$!_Lj32{E>uxsYbb5*GPFH!p zv6e_Ih@X+Y=5kXg^kMq!`SRV_ z7TbH@;z}nWCbQ4K{ifQGZZZEWer(o=+%#fHWnRS!nIsKj+RqR$1}8heE%C5n5C!s= zv(NGC1`STiN1i=Hhm2Zt!I(Um;11E~p;iVY4hi0q;R{@A!|QJzu-?qZpT1?-HJ-c; zJrH$Fvas?%|B>nVDg*AtVb_TP*^(JT8V#VV}6Wc(KHEx+XCgvhTY_bigtFaV$ zx4w!UziKVYNzlubO3+v{(xRTmH$NIIR*9H#QH%L3Ruf>NXZr`qHo&IAkTJEM&v1H84dE&bq7OL`B=#E2774Yz6ylYQSHJ! z3&SM{ z@fH`alTJa0Aop^)n9k?3A-rHe)tv`KGVzM}q9G6!w4!6BYJf4^RHT5tM@-^U&9l0u z{u})IOR^SgQ}Ym6qvS`-4S=#()|TyNIm(bPe2NtvB9E@>GFYQkWmP zv-ef{Ns`CXux0fRO|$G73C00LmMnC3m?B_oJ9Glj9bkO3)26Et-UD}InJ~_$2+W+! zCan45aDsmt;U>v?do^-{W`eq5GeNUhWZ<}{S5PqH6N{VNU~ZF1@Tlv1TQ?;;sN9(% z32{*7Xl=Zv`Jh5MLQ>qGv8!I0v%{SiCoNkf7O`Cu*Km$nuTvSD(vSCVIuH6HoGg!<$hOg4^Yx zkm(b|h{LvSdRFxJ8nM8SL`(#QRa6z!J9NQ^Mb3O@gSvu@=49{fhZrLEV_4MQ6b3lp@oI<62w1->0_s3k8}RT=dTlbz}6 zVuYwungVMjFRR%VB**rO^X+y#GG;^v@$0WyNtC$#B-U7mSo?W9|Mi36yE0fyv11 z&YVQY6a)GfyyRD^sUn}H8yrrDa~uw|IpE$|A?P_L2b$QgM2DAenIxHb%aS)P@uJA+ zZvAMWe=9i!)qQoNvrFFTy33${G2WYV=j)P@?+od>0^MF|vO>S(9JsDi=w!05H1Dt6 zC2%4^^!AmBS#5CxvP-T~mY04KH5$hFc8Vu!P|vPsV=2tM^H$o)ly$>~eJ6*h?bJ@; zNtgZ;@g(8@Veeg+jEoxh>%DlqYFgA4XaJ4Ruz(HJ^+#ZBoo)UC_6px~9 z6gv(poJKY3UExfyj*n_6!TAl&=W)bb zZRm9i+sh00YKMWj9Tv&>Cm!;`6%da4YBC$0C0!eDP7$o^KU z_29^9|8PV3?$TLN^JlwPxHea=iO0pX$I}8u5~gk7X@JZ9^ni z^o>JtK6+fI_~FUZ2{GfciQ(MlmM+3<=@B5yq5;H{V2j(X$4YcT^JcNsoLD^LhfVyO zlF8dH62@U728=+Hs@!U7$c1n?`t0}jU6uxg`-af?f(HLjb--b&vjzT`!WUr68IkdF z&?{}idGcsM0>N*FmWl7(qYViryyUZYmk9vApn(y^Yl@fU8v!_X4F$qQxo>o<-`$X& z_Nym!ez>3Q5aGJiuXaQ*pD=(vmdiKv()}67)P;!L929i72x<$9EDpj%GX+>WfEJa& zu#wZw38FSaBwxgLSC5y$ zLSV+WpDyXq@`wVRct1C<{n8#I!$rcIWY!Nn;GK$MhPC&BFUN5ZdGg4E9 zS!9x#n~Jlu3SNQk7xNCQfdv0`LGKfOexaa}`Z^)P$zz)?`>;M;+%9(UJRqel)L-CI zPKAVdy$%+6q}8lT>-ne5=i1~=Xr<{{L{(T8sBmD(rrBdtQDP#ITda&-T%31#SKwlU zV3U$P;NH9oyE_=_k!%i*pg-DJ%P#}~^Y&T^#2HL*1;)nI!I3I0+EWKdu{co|=d%b~ zl!MET0|~g2L4*e<<(EVRYLUs$gglkDP_au*o(w6&3LYS`hsxYUP?(1jhV_b4Y*h%9 zBjZ<=A`uOeBcUhwB9Ra%M<$*$i=tO!; z^eX~#ktpz)x-?#(9!w}UgvPeQGGtA$hvK_+`N1uD7~<*^iSuE&Ks4zbABHb=e-4Q^ zh__xNi|U9r$dooY@49z{g~@d^^AyVdfSN-s)&xFxf*Rcdnk*hX?n&jJJ!ncK!8P;x zY`<8tfp$Ef)1>y(cuY9Z$0M2Qp8WV=R5(6crvcr_aWd7IB%UXDR)sux=k(P3#}U(c zOz7$NpGyc~$4eMXJDdc{;ml}h#1xD#Wi%Zqz~?sR1_$(aCwI`r=1$dC)2QXx&k!^~ zAH=ynr$WlOehL%8rO1OB9%eO6ileNBD4)^wjH`8kXfKhRBXq-_MYxdo>IODFH7Nt76ouhdA8nsV%MX!gAIQJd|cWa&I91MJ^R=5r-u_7W9Lskp1av) zfd0)x;N3ZPi8yg{GKGMUkd8U3r(K5F*GQ5l zi8Jl+s#04GqjADXqEv|yPebN!axd^5GDIly@!<`wmWi1c~wnApIOrI ziE_lwD1Ok4lj&-WlkurZnp17aqL(-M2F9=#(>3plsx&bbvi@b0%G`F=t!yG zY{MHGe6yJD1RNbupMN}P<;C7WwLQjQZ+TD}?XO&PSu0D`cXbO);W3oqP6!!mK%jF> zHdbCwZ}QK82JE=?;@9bx4$5R?N|%t&Os{LD{Q4_xMXhbiK*%g=Y(Y!pT__yqBeG0&7$(L)$! zI>pRVht#GqXRRPVibdfM1mVK^Bzx;MU$4%AO-xbPCZ>Z>51cnsvKBKDdFpQY=TCT_ zWyLXX-bmr^NUsRq=x{9IF)Yedeuo3^5jcZ6AO5_TL|G-Yq^(E>2(DAWNI|Cp@vX8D-qpyY8b2qqtg& zyU{b}%W~hyt!Nd&b3ozs_ed*E$s}oMRUDwQUSzN<;bA@O_ z2uK1YleROltMzb&rkM!=6~kd^8sBH?+;LOlNhPmHC!=?THNKb;nQ88WE* z{(-H-;Ug(wE5f1ju&r)&l3y@@SWbtNpJ7kLh0w#~+q>@5L`0i`HtTa-f;X_)HIXAm zw~0#d1#&FX7^k1&)gxrYcEcBVYx#vEOuIu{$}Ac2SED~=V+4r2HNJS5`{^Y=H+sM zN8TgIvmSl8NC@Cmn1*Wv{9i~O8h^6^rV;2_ui0g$_}QNXOt+N5&ie1qRtYk#T${LV z3Xfx+;y#29Kl^0HLsBDV)ORl_VK%~&k(zB6Q+@yAKIm=374|Hf>*?%W41U(LuOUZ? zvjXF)B6iwbVFl1JRe|}Llj#?@KBMUT_mmhnYVi9+mPksRRUOjBdJ!Hh$2Xr4IRdZT(+y`B7-T7zsA|l7V)*uH61115?4uIE`_|$_s zcIWdqfwJ2I^MI4bu(Eldp@RHa;-A0MKk9qGSt2W*LDJv35BryxRn8!x zkHn@bc8f$r&QhA>XJh6dQJTNvT;P{=zbnx?&T9x0t91zh)VoEKNJP1`Lp2z{(ghLe z4DMT+Ewn1ioD%no`HEGl*W%>}gp3dk_*s96_}gW7cCL|Vkrn{`RY=k8-ayX{C2MFa z4Awvx6wz#Erwz(#>-Ei$p?mDh=_b>LOy?P#Wd1SVOI<3(nWUJB<*CKRTz7qM; z2UC< z!HclMt$$oBBmr7E=A!4axOc|+zoBhbBNo9LtDzACzJ62Bel?~hfgW2I+k6nk>Uezig&xn}PjUac9oq_aMlSMpG|P04FkQHiPCmx}9?0hdFme21La$pD!cId3vASo*QXgVu6T^95bT&4h~P)fCxk##R^f(3)*6W$^pJI8T%9yc z+FDx$Zztn1Et7R`Ob6LBCbO3LG=$CSVW)s`1HjbxkToFmGc@w4tXFRiH{#l!GbR_b zP2zcbb~@c4f6(0y2J+8%P;+xQIbEO7J2p*F@$pxl;(UFA`!7Qjam7T)a5>@!yc3EcrqQ0GHbt`@bf5?U!~N^2fo^Y`t31 zv%EN`q0fxG*wEQ7(g6`Ay4!Ck%XRzYBR`Cre6=2g$=UhjO8n##ab8K)c;jO+`^2$n z6P{ymbAl_Jkp+i8E)hA+UR?xC+#?Hx?4HbV8HaOV&wPWOuVE7{xjCHN!Bs0^Z*R5; zPpXv}F%#zzmOTTOMhW31$I_)l@Av)gZQuQjM>hNQ5}rm=rTr5F5uUwn`!k;*P^iJ> zCv4sBapX+=Bhg~(i_OFhj8#<8Kl<}udiH_;;gNiuZfAGVM+AuN_~m7R*&#|1r$CSB z=j0Sw!)TS4qUjF&Y&7t8jkJ5+qib9T7m)qx-D>?|g`gqzQA83|+ikax@ZWkXi1$>V zR7!D@^08mv+#tO7(VmwI^Nok^MfW$_UZ_g;eRp+ou0G(3e!n|DRbN(+{c=5>^C$nz zG)qQK3J~LTiAT8?e9|(xU7vQ>``g#Z@$~wVyDRvj^kTkSFHCI(ML#e#QwoRTM>lE@j_jZ0Y4b#pBH-klpbjzqe#1St*%S{L6HmZ6EtQ;= z3T*4>Clr{{4n*xX4BLTV5$5~p67rLiUDR#AD?&>02S@#<-h#|g7mRpJyPX~Fw&(^Q z>A3i`TTW&EC}J}89}Kev>J0OJ1I%{~Fi#p_p5|dL5%FhcHXO*q%~Js5L1W6SNZ4Dv znc@`hgil}~>!`JIKBJ_)=ug-82x;Q&EUCt-`(TD56Q_92voWp)XWp_u#u_CfHs+2% zPY#_EO0L*ys;T`1*ACssT^+hcKB9gaVf*c@v-Um0f1hKv_?E?Cw<#NB43XiDX^U>D zpPZdJ5yD+}4#h;QE7cGLUtvmWu?#~ExEJk&=$!F3s1r_n<4ZjIwr z{{e|Z#W_XP$c)9SJp8#`o^}I$32cK)#5B*l?I#?;yT&6qV*ghb;_WgP%N#vyUUZhe&y*j}{_Nv9g?5gIr74=iq+j=3nJTV)B@9}93=bCymh$P{0cJ-lTLy{b{q1FlZ7OF=~3is z_Vj`!sa`dM+LfsVbYhpECSbdsH2{yG`xG~#sedqG&|L!26#fD$i*HVfoc<#U#F%tQ zG`#ID)@w@GL=+GfV2)T4dP?0;k4fBliI<9JcczDc^5PVEn3iiZrPCP;f9mh{J3LS; zUHM@<-N1myc1g1ChS+==f+}~&bP2+SYNcP#0BfxcQ=3JgB0j_pv0nAJVU+}rj|EDm zBqAiS+X0{a4)>MpQ5^H;V}`aWiD+|4!lnIMB9-S8i*8^rJH}8zqAnKxMz4zJOKHXk zB{;(t7*5aZN7fsw%%+D7yizI0moBp7&|uB?vi{#9x^SX!{ohiJ|63FT8;2fK9Go1} zrJ1X!%V{E$ae=t>$auq(4c(TF5f{IOU4+3=Rm=VjHWD5+C3-9Y45GIH0&D?}WLTj^SHEh*25iVYcmMA$1Bc}Kaxui!r3|o~VF@#zC z@?tr?716-;kOX0pKjN&%^5CLO<%^1?(>iWUK9Ys3Yvl^&jEL~EK@aGutX zcf@}ee%BlN84)qSxv!pfk3{rS;AV*4_+F-#NKF<#qDr%iTjhH6^MVczOtPul-zrY)w5-2t0EP6MZsMTaSif4uow4s%go5d1ioEq!QO3X#y zuUAIlX<~AJTw0UThJ#FkFREf~v8;jWTe+k_&elXfp(w`($zr@F1#Zu5; znI0jL0CObrWkN_6Cc>n9DAaieN7OZN3B!kYvio>wF3w1iw^{{JRGRU~LH>5t$(15O zhat(^R_iJA*sw~mdf;9)pD>6-c>MLxXWLv1Vcg zB(=|lQKy|+$;hY1ywZR?hr@S0Tf;#@vI@Q^r4gPitZJzJ_0Dg`|49v8K)$uLBp5VDMor#)v&#;h8u9(7Oyv;~x7MQiuRBwSWaYZveY8t}(KI zT_Lgi4Bl{eRN+b(EmK6BXl83k57Df;EeU41mYoP5LzrQ|F0p7nq;C>_KNHnLN@?*;e##Lg=m1@G-@_;^G9T0hCxD87*-54uweu(M16JD82HU4W@ zh58<_d`n8Tn2}~WGlJZ>q@-BhSTCJ*;66idd%PB6)WUss!_l3!y~ag$Sp-&nkI8;^ z2UWq*8%{*%*{6Jwb5)hgQ-4=X-w{#MD0Yhx7qP#K%~&u^T)MFre^eB85pyedmk~}+ z5k33`HrYMjW#+#Yz5kt&PskX+kpq`%a%3L$oh?>%9vs0`!j0mYa+t^$S|Aszjym#m zy0e}@DcholsgUcjB;eu~CUM{^i>3R*EDlq~G3<`Ci1l#PIFWVU#B zMwHBbc1wi&;?!0l>y^U+p0BafJC!CO!?{zC-u$5$!xf@@|0*$Bi z^s}bv%|4jkh7sohab|`4GTUvJuY-LXL_w}LJ@(|w6mu!3q7Y#jxL!)uKj{Ivbl&ozv3Np>)Ze-@XpS=sv6MDifh~Fx{Q1B4S(3vmhiWY?dfJG_vL) zu0DtGT1Pd4{_Zd?d2X<+S1h3*lvAPX9J3VOa3tA<5U+8l?~j08g^6(ooi&Bh0cO{7 zwZ>OZmU`fD5^RA{E?;|t;-N))+fb^JJ?hEp0=(gZ=bBv2xgu|G_M1H#zwe*FesgvF z>+#u3!$A>CkJ@f|Lu^_{C-hQFOslC9F=D7za~mx3)*5RrY9P{6_dV28_gU$w`9?|@ z5SbdyH|0?v1HP7Y+Z9>e#s?!FS7bXVQ)IDX6#E@00iPT?A-oU>d=v$uQc}X@F>)IRAMSLjY;9u2-NX8Y9#2-X?zX>e2NGlOCs2O%`|AKA%f(J_sr)2#TQjLsiy3O{Or{C(D^M{l;IcGhQM+>xH!ASf1a z$Y@UkAw`?F#?=hhtUrOY1oL$C(WHfpE_NHT(ZyBw6_2O;RtJj@iNzf?3UM2)O@GnA z!d3!lt{nmMiFeuNBMexz-ozi>22~mCVZga6pk*m0aDhy0fp65+hh0PD<JhGc>z>83)n-;~Vkr&wh6yONmy6bZ~0r1DZFiMXP&sJ*H* z6;@nWz}2F%C>cpPvySZK78gdX*2SsFlF|~k z7MerKNzIuRf!N$vEjRZu5u7X0l2adJ(Yd0G>~vWwytp)Ozb?;+FDWkK2Lg0x842n$ zD9nO>$_rL7EH*08KRJU8N+U!>&KPYD3h3IBDG9#QF+m>Q7X-&HHZ_1u{!x+h;b)E% zLc}aUd_a%~QYSYo;O!fn(27Ifw8Vclj(+|gE$uEd5tg*5h|}+jSfY}NPQ{T$REZ=e zF|tY{k;B!+F=~=yB1a$vDv)sjIhm%X3w}f-ybBT)PLz`|DmwIKQHdT08IIH_$&|*K zaYZqs8gju%u*;)E6WRd$>y35(mZKg#%r*)}4k@IY6T%GaUb343hHH2K`+q$Ni@=>Z zB$1fk$NMd91=y6;XPx19v2-DX@UuEJiwg9q85ccADC$NmW`BstC1aMWEPF_i)Z42{qb_-)uCWX@M_1$NfG; zaj}_~6ngdHB09pu4G1~p<1f5JaQyM4ja85&j~))^{hWe z8fVL{`FKv7&iJF54ld~KCekt|GK@Q&v~dh4Z8k%)6sFMdvb*UX%%@PpP#KiYG6cB z-y=VELd1MO10oibQZW)kWD0Tn1+i_5%^lq^<**3^T;sJOq*Hc@x{0fI{RQ9Q)%=*u z3MRugLH)7&M0@9Nr+{Z?0A4HpqWI*n6w!A0{^}M)eOnz8PQ!HvY5y=ES6oCs@peCCI1g`__8CF zLUGn}9Fsr3#wgf+njp}1x9py;V1cdhJjgC>+4T~he%l0OU3d~no5(aL;$S{eE`N+2dMDrHw1;CV_e}v-j&CZ0pdo0M(!`rWSlmFy5jqE2+2bj8k6XZZ5T<4UxnhR^WAG?h#OcFs9m(hDpo z#3?N@t9Z`L?92peErm(Dj(s!qwT8N`ISYk9E(A*a&QpPoK>g-Z;LP z?s!M0qg+4T%jtE8n;Dt{U&VV4C`!R%W``62d(K+1+!`%FM3QBjfF@NB9&!}HoADa( z6oH_#Mi3-NP-z%PgiYOE@@DFI$I&FSfthw^=rz? z$WT>DDXt z8^d(tmv0RRg^Xp#VYMY9P74|k3HUr);mPlEF`N~1O@Q239(2)kz>aR28pdME|2j#p@r+rLz2Z2Zp<0%6sUnG7cU)AOM++Ow<{## znd5R3`S$5eVYh%7Z@TVwY9huTEP&~K4?T4)4M@f!!*sIiz(5Tsn9;IUxob}d>Yga% z)K~`c(B>(nbRnD z+Ndc{teJZSSE*(T>fV+WxM_mYz!$_N$`@q+4o!pbtDg|Iv+pY|T`DC@pPk3HZS)cIf^jGZ zyo5z~pp`tGa^c1ZMj+_3bt0Ggjv!6Nxg=(W-s}$h*y+=6DW%8T)f7i{G ze}SQZ|3CdUn`G&?Xxf|8ML&-Hu=XvsQr< zJ<`s8dh*@3J{&YB@3FrTDFN8SZ=XWX)l~WN~DsD2cMj_BH24_X1CkoTA12eo8OLfeXM%vyno~ds8 zm~N?HJU(CY+a2bKwQ@1C+PwcJDSinehp=O4rzSoxPg%;4%&Sxcp8!$}6!18KN}-IR z`f(?^P%0S=N~P!f{;pc_a{71p=*kVk>+NuOa}Y|Y4go2Z5(l-`(Z&f$`8yoW6x%2; z$2TyhO4YoAh_2Svrq-k==rukGqRu5jRC%w@kBKBXm%8~YdH+opkSviw4ZcD@k}D7KG-Nx9o2mkTE!!(I>4GY+jp zRxn~vYSK-jn<=e=YGbB2+&Ysi1E3DsJ)DOsDaA&VxY1QeOJqNKxyS8eT{z>7G=F~f z$)BYXT`9Ua`96jJ4&hAc*W>Ub_8gbL96!-;#GQ)9ARW-f27`9fF<2@T0eq5pqAabX^!K4dwmkEB-~^y=m16X(XYMJHxMeQq|s zwRtfyL{M9(4zu0eZ^+t(2u5=&Fc7pZO`Kk4Y$*bPw<#?SJZ)*Qa?nvB{(JH*w)C3c zZ9o0vvHm~-8Qt@f)63(`cN`&vUtwrU#th`~M$*L{vK`?~c0?h=`vMdH6~6E*4S@_! zmJ2woe(9FW^%=*)g9bm65kmPX&b+fw12)O3;0nX-byQ9hy<|WNi8?qR(jsyL51j4_ z7r}g7RHi7M7FY%@Rn|*hLN2iiK_pj}oeNwVqxZYB%+S*j`4Td!s|1-FcZ3nL+lCy> zmcA)YkbRyrU(PH9V=70b#%M&Vz!7#bu~ZAZxm6OLE9!*;7y^&+B87p8S{ZAoS;Bj@ z?nW3!ebnk!sMc`T^Py&e2%UFgBVyv}V?qtgWyRf!7k5B2o_DhwO)L!IW@+*hClms4 zwH}l_yB9F>Un1#ZILtg3hZDS-a8oRXE{IVDrOBN%C9TRJgl9yA5m|K6p*umaDx96> zN?7(3bwfsJ20$iY{XALNc1~)cZii<|F|gbP%zQD~{{tYr64hW{lOqCIpo1DH%zwJV zVo;WL43f$hHc&*yUJk3)1sR0lUOr6v$^$A=%NK4u4d$PveU92 zuR={GmS4+;(@G(ol)>ot*9h*Wl;1wEm|dk7AZ7}Wm0os#E0vl{2f8?NQ7KL@x*4vb zNv3M%*qMxO*pBg zQXxA_CA7*dWl*=_&vlz>X9PDu752VUm;u5mXqh%CzhfkCdj>;9_kk5qKqCjRx8YQ- zri{bGQsW-UQE|xCLI^lxma`r*Wfnzhe4>%QIIB{SKO%-MkW=$^4W}_@mRH(>E4Cjo zj&Y=I+QySbj@1mIa{mbM`MTe^=2v7V(6A@Wi%yC^W#jRFGInEF@kb=eoRwxA`d-G8 zlog6ETQ3(B`+i2*XkSC_KCV+wVyd`YHMNW#aTOJzDZvREd3JFnEp!A~b_ozR2sJjh z(8@56Yh>O6KanMuh5W9hJ-QW4Gpo;8npxE*ZlSt%JDjUF0x3&*7SbxuzHd%0&*3SM zxArm$q07tFu`&C}Mq1@K4l==0$|ct>#r(_WioHq#=n9%CfwHw*ZA1VZa?5 zHplo{oL=e*DQ7f2nBK9+8;`6ma61_WoE3#h*`)NOdv{NU4boP@U6Yf}!*QU&6VkYOCMtFhK zB_y6l>ZkAVE}`;&H6d|k?z9CgK6IIm9MsDd#6=Hbv)l?Uf|=j{D@P(LJYI@Z!@!P1 z1gD?ya4QhBaMFC!R6cJ3qnh9yYz+!vKU-bCcK7Op`<&=_VxTKJW^vV|@)~0>Xyb6Y zzrX)9zK;zBi6!?M{AT;h#YJ=dW(2VA<2@JJX>mq}kbtNnO}ZBy-HgP07}M?YQ@mF| zJU`9pbf8ARg)}N+s}IAf{M_8&W|VZ2sMIW@ghnn0w!_T9yOkNeGV2YT3M;JDxJ*W2 z@EPc)s*1YVgdI;794;GNbr@7rQo57+FsZQOv13;B=%%5t#QWbLit~xNT!OO(x#p&B0?t9*E6a!pG{F%5_${TIE_;A6(ML)p zNM#yglDH*EU5`UNnens zd+{+o9jVtjiERG?=*a#bfR6S61JKbvpbGs%I4{&;@${@x88?IFd*O!(y%IYHekAlL z>SKCmOy7JriiEQ}ba#2G6+N6>fFf9Yj{{bar2ce|qPg17;AI~AC6O5g`a1t3wFXjQ1g#&*U4bhMV$prbXU z3L2|y4LDj9YM{|PUj-e*LZJTj-iW5|8Vf&+VIi95YjmTjy9zpn1%R%rQBaKLtXqIV4 zi+iyZF`75-hhx3dox-rzY&OWpN&HOta{VIJErca>qnt}4%?CL zjHx%7tZK-^s#l(16kP^tqE(wTLV_V=s<>v}Iq}CX+IGhF#TO*wMyOfer6Q45^I3%A zCGInLg5RyIjAI)}#476fFmhj(V0i>QfVNplib~%1q`Ua#9W8d~Iz`4DqQ_i)#UGolmo0=a`DzK5oHKWt(?i`dix*c1L{s-o&hJwl_}#$0@%og45|zU>p#j$e3B;;6)T1n+xZq@+X0U z1eBoYDLQ?8@-$VgQJ8^+bkE^_6bDslViAb=@r@tF@)x2RGzN++y)@9C8r)|z>C2G~ zTJ;XGinYkhT={3T;`7M`++&%1p(0WBj1d)$QlF8y0ZVagY!W4}r!Ae}kPN4wOf16a zhIIfTCTk7Ctc0?#o_M1ykktA)&ve}!(^uh2^`G9JojjL!WDo%L5;?J#m8*ghf!A_Z zg&=?KXoaW;tog{k-==u!2WvZJ$H?B1B9Z}6I%nfFP@+d^SAYxUNj#D=1pBKs7hh6> zB`vlr5w0sbHObV)V39Ag2nLh0%^X*uWVr2+xzbXpBDQODpsCx1+Yuc;jlqol8sl(3 z7yEPCEN+y~q!3LUBU_(%Hb&t$)O~|^$H0att9;^8%JD+G9*W`kl0o0A zp_-KQJr6T6Nh*Xqh&d^$ftYutY9QtoZ5D*LE@7)V%6^l*sY8!gYEET>)?W^eP60!= zMd>F*kzSOu0C=HIehu+u{YcLEdCUggN@*N4>ix8s9hw#?xzMO?>n2&B2`aeqnuXBG zSF!-OE1}cgXciBS_!=zFaW>s;;3nWKPqh;9U4%5Fd-QSsX>o0s8$pFaRs(yRJ`a;w zOY?yK)(Bn93R(4KS6d_`C>Ot7EtAru2eX3GrUn@FshC9$(Z=6DgeSTTTf#aQL(UVt z7LP%AA;JaG)8nwx?GxH+QDUCpy;!9ZGh{W!+hw>q@Xd04OOX8Y;0W0qZaI?<9t>cG z`-ZYtKc4Q^_Y2BIeU6t?5O4qI+w}x42&72pIg;ov)Mu1ej;IAxUM#1#efs&$en(F( zB;fL$N97YPy;&n=0g~6yU2*=f*sZ7hi@rxqbU@EswF;?bt4iweOs{jSjTFX9JZ%#- z&IEHq0^zU3G&x7%$VyY!aRezsUluH-R~V)&dILSI?!} z>80&*A&k3nA*|_gAzv?$r&qJp+J``W;la@bE23mxc)3`0VhGc#pgAbM+}qFc@>oy^G^S)L*wFNFPOhS_CxE4_`WGVYU;>1w^Aw=O(W zfQslTzPR2DUwCbPm=28rT%W#Qugs(q%I2JubGBacVyhmo*3$KC`Zs1v|Ecs*Ab@Lu}D8H8}pZLO?!#Q2xLROCiw$KWu$+oX6mT#~x@znIwr{6sRDfzVM zagjb^Z-yK#xW36>OQ^8I8A0&UKsb;8w#P%2^NoG! zaB>5W<}XHGNg+)oDCEJI#a{G(I)^&Yjp^U+kh-$x`6>(QXAGK!xxJ5(%YC=lAp1Y8 z4m&r~Pg2q{1aXyzfOa79FqP4~6;mL#WLDt&vCL(T1500tG~krz)6FtUlzY6CaTNlf zKcpfgM5pZGMpGo!fiyH`uJ?tUkzzaHA2ArlzO)fl-}nur+{O8etwAa6C0~BB48z)?H;6Q@Y8!AllDsf8gqjeTTTfH;$o^~ zn**W~sXCbjW-EI3yUzVK&7Aw zAk2jBT~mv5fEjX>cRuQiY=iUjq#t4VDd7X&HtV);R?AQS`+q%=>_%yA0g!$4#BE;4 zYU9la{exrFSvUE4%_;FHt!dVWOeWgnJq%5l9eCw?zQD$2{cgXxK!T)E2#vr8M^wob z9xp|nM6O9pmYjdP#M>orzsnV(>-Jk|&32D9US3V*bUAdrxr`Xd0oEdHr5#>b#hNnh z^N;&5i*bEu(PBQ9$%7+40=dB|Nxld(1C@D=E2U`!o8zJ!WB91P>HPhaUNadf+K37s z0K9rx=E2H$g+{`5*>=eJ*KNfwfE+UG7o0Y(*n~Rzwu4#PShu|A`B~M;y98rMD22kI z9GuO_S7kQ@ZEaQ*9)g}oT-xw<)lY9427A8Ogob4Y7wp@ba&WYu_eVJcCZDb~<3(B{ z^}iuSYOY+#(Js;E@r5?#6+Sxg8jE^Qk2`#TJ^y*lxWx2?T~ZqtPJ}bu@Dra*D`F%= z84hhzeqIYOKjJv{b0}=F;9ya2YXNPOY4CLL1K;lRFCGP}N1p31M8@dA=ka11)F^;t zC||=P)i=v;(f`Nve*NoJV{N8^jnMp*G9577Z(dC|jYwh(keTxO#mNJ3{@98{Y_(ZK zMrX`Cg(ztl4`bN#0mEZx|* zw3FU;)B8&-O_Zt^PNBA`LCwvGQkl{tV0d)`XUp_hV#SCH8mWoEEL$ueM1{B>(PRRD zMXsI3mOA*?FsB-El6iwO_0kpbMgrU>JNve-c1oPQwY%(Y@8Mx-tkR8z|4EZvGm>Z> za%%6lt1+wNd`iX#nSr|0_5H`*62o`ppF|!$9SSJ@OTMjeD~ zet6@a!E4zBh%L{i;D~U1z9$DE&TnvkqqIYEu&PJ;1rnT7O7&_NGQ;#(`|TRXG&#_8 z=Hw-fSA`MNEl;*(-;HX4(h>eO%T*q}ptg9)f{l$Z@ohzDUg)pZ zV>qcA4#f1+$9D)cc}L+l9H>;8#WXlxRpRdD2(v3BxQB7jT-U+T{1eiXFJ=Z2^Z6@0 z%20a3+sfCniZ``+&Vc`juTe1%MWJb~W$1CO&9w>6uhZ>W!8sALCkSWA=t!xBYucD; zP>*zwOKgsS+=MEzF)RVR_lC3RvGsM+?d*;v(ukV&`x0R}BdfjS9zRuD@DS#qG@nZ% z&&W%P%xG$pbT$SLM-a70LDn*|0mw`mQHjy0=8A;1Uubj>EP{H7{Ib8f!P9s$!X%+c z6OZKMBEM?Ou0q#$wqkpW8#fkLD?ehV`))ta2Q%Btu{Jx|hF+`=e2yH>8 z1+6|X*V-)3-YheKghIl`t*?LnP6XuB3 zL1E5$)*gYYuW+?A>P}W^QN1RK;;z-jdc8Bt`|H}4TTg*o8=r&>@>+IN26!s_`&Fl`4!%oZ?)t{?4Hy_cQ_ek_#7MsxFd}2 z5RVO+M!~R>TFbD-h2XbEiE(Jr2Yn)1hZXC?&J>NfnJ%_uZi_ca>ZH^ela?VGBHMMx zT?wQO_sQLU^{&Iyz*Mzbk05Ka1&^Aafuq@%nc(>Nv=y#?HW;igAVVkt z6tgI$XpD%D^g@tVloXB|TIn`Ez2(C@=3n@m)Mb*z!KJy1AhReQ!`!S***`eSVyeoD zs9D5`fMaJdqOJ-~z7qaAriKzio;)cbofeF#p@LJYq&Jro!x2~P5u}R9BIL>QY(An7 z3h;U2VwR7fUP7plXNSnxDhf!IYB(ZwY&8X>N;SrS8Cy|<-NWAI&366q(}Z;P`Re^* zyIvKeryK{Qb%F+K5Lk4BvbL0|dN7HuSDaRb3E*t-?ZwMhWS&@YzV`6})$RN4ftZ5^ zm1>^55RDu5^^K!KB6obFFJ7eH%doV{yld z7zcZp6x^aYL8cU3x!9WN!A?B5)+5p)My{VbhVUpk;PGF4GA7V$$gsvw<+v3ac489_Fx>UDU(6_hk7vPv z!O6*&lGhu#E9n8KtrJN~Vq9siiXHAx%k2`rSd5V1q!sU0>kq37x@>xd#Jjk4c7{L? zO&nKq`LWv*x}&8^NN!v}Vw3z(u3nfE0T%Q1eVHnke>YT)q$%|po(_~Ne6K2j{IOdy zjC)cZ^mOgNPM061NM1Qz;=ZcmYQL4(s5cw`&7+YuS7i-Bw!aAY^k2E#6i1fHMzJ5jB>vTpNedY6YtTIViR%rLrclHK20=OwAC8*o! zMR7PK1egz1mr;A0u7g`X?{21ut2(*sX75soVQym;y@JwjY51ADu8Ok+Al#!N#S$Hh zQb@W~|7Mj|gK;29O$5XyMqvgQ{V6X!Z?>2FYnk`FV0!GFH|Mym^Z{27{b%&gs)Loo zyMHzH7y*DoJ?StsN9x1cp;VN9D?#%PGRNBl4U}R+Xhe0r!8&Yv&lk}?7;}uC0V!4s zOFnV~VQRQncBU`L?(q?_CS;6LW1(8hHky=*yTkPkpU)&jDn{FlL4{Kuhz0fv-e9Cc%amuI(7n$Dm*`k;3H~tkug=%& zt3i zKoP4p|<{Q1d-vo++GJVm^kz?=Zr!DX~e|%5)VVPv>RG7l_R5 zuILywUiMfBoa2y^Pe#&ZG+}olKPmuu%2ctT@PV>}oOaiHwF5Icp(>)lJYL}-uc$U! z4Hcv>)l}eG$3`ZvSd0t+BtMJ*SPZb*1)@k9mJOmhE-W8`OVVm@T#2lKjy6v~Y95Gq zM-3Mpyhuz%^+yE8U47aivzVHEA{fV}Zw@z;cFj!woGy3-Cbwt<^%f98W2jatRYxM5}y_F zb4{)nw@{eHbTy$fF1(~Yz!xU4Q#;kBL&tK%a^PHX9kjvL9h0`5#QkGY69z2Q2b$W5 z$?wV|+reZSV(A36<$B^A;xP?~y2^LBgDJAh)eu+_f|;dpkjnAr^)Xx)h6$d|!_nXw zQIN%om=ys`VBW6St7NJ=pMIi5h#tyUqycl~zab!K{XwE|m|6tXdn3S?M1zbO)r1mA znW1Ey9h&u$ug{@Mxhx(lSq=jUICGOm2r5%T3naz__FZe}r*b2D$!W5_L0nEV;eblB zObm$#+g6G-W_pil<-fXZ;I`r_xGyjhI&F)H@RV*TMID#x9t)Zyb8&ekQfr{&U~`0E zRZ`yHDP=*;(@G%Pw4sPrPzftN?gfqPxi&&N-}iSombzaUyP}Ps5?0KP*CvIL@DOm@ z42VRCc^SWg?W2|Va3aQaer-yjFFNETnTnO2&T%QcP-mmO1)QxE!AZfbC}@OHP5BmD zC`4UKLtifXUDe`rkWh3??zzpEB^p$%-8$(aJBXrorO}`))}nT|ttj6WH7cxy-skwY z5wQ7pY3xkR=W~2Nj7?7I2-X5ow84Wm#)Vt1kW((LXVD`qr{|vs*LyN|e}msq)jxQ# z!n1xRsiA|I0NIrw12f*kf!8xqL9R6{j2$PY-#FtRJuxSKFFXkN+hTV|vk_g5w#}2; zI64!#FkFsq+$vO~gDd6BYtqaX>^C?}is0a!*N$~xN~f|&FBtMe(o3Xz(a(u%=g z8`Xt5#Ij31z1A}!p4MTg5la#_!3``TOgzX-acv)-5Vep0&Tb8q2(de0E00#=23WbQ zvz4jc-$AeFpWA*EH!2}F=34wv4ah>lBH*C9xOy*hoAf<~P;~}<#52oxHAM-*xj)cP zvK?YKux%|l05U^HHk@!!}50Wa2knt3oX$ox1 zf{RX5bx;iy5&d$7=n=>>nL;x4!=w+P3#*CdCaIx;D+*x8YwL|%CD08uAeKNMYh~KX z5Dk=Q9%0x9S?tzpIy-NGXkPeJ+wINkrmG!x(6=gfjJ+^Be#ZjB zBd3UT45X|*7n%0C+L*KYS`)&iyBMtuA#Cc2o5>@vd?Z3I8k8pJ5bgt=fR#XX^{`~5 zqYFLdOdc#Jq;4;uJc;V4Mcr6wlwuWec|AQ%<`maRRY7+j@gy42<){1Xy4&W(6UAr0 z9|99)uPP~`T!k{JQPkJEUR=`Yd}ipiw2~0;s}wZBxM?qq}JPY z8hYXe?=-myYQtk3$PV_O9~M~=#`P6c=frIxDfWl*^Vtq<8`$jLrQ~OV=9%KL%FcH= z%HgQso)1@g4rVqFg^j5OyDt^o71hN93`e$VjPYTE2QzXiHaXUqXu6RWK7?kfP<0-Z zE-?=hGI=HSeY5!(M#ZFIqiCs{8nrnTswzj^Uy z`q6D)q-L%1D0(4;!-VPx#gdm&l3j+<-&euV<8*FmZOX3Ik2I!XkHlt#-(W- zIrrq_M-0bV$GN>ST;RDjfLMApsGNBe)QV{Wo$=jSE{&PbYJ*jG9FTsK*Vcfb;g**I1E}QIbqV7A?1D>`F_wRkmG~B{}R0r|bz+ zxw9=8l{7XJ*qk;x%CUGVjmDVAto-a#W?s%!;lW4LVFX*)!{@3o81Tw*_+?Y;Bb@R4 z+1g&Of1?fALn<|>pItW-Xj=$Ov#Zf{6%lK^@oZz*(;XjDp+sCKZq-D5QQ$Z+>KFqQ zs?UREd`6X4X6qk-AKwR5OG6{rh7zH%{1VAwvXxntqwT9lPPD`p3^AS)S8JM)X+rMo z+sNVM8vuk8HxoeITaRY(ydF)ZNqGjn+fp=@bDQ99vp?~}V(**I#A?tU)Tqzx-xnKc zn7Mp2Gj5E>R6@r>Qd&jKHHj_)+KWSVL!hf!buSS*K-Vf_FF;Uu`2o+(IqgnS@rJ7_Ri1C<#WQU7 zRNEZ4iffI-?PAP*o}zAITy{5=C^8SdkPr6#7gykNd_88T@FevL#}JW!7*%l^a@SJH z@%8+Ev-=blkP_`8V_#;2$bd5+qoHEHa_?OQ;mLL^upVUsxT5ki)h+5?eKr(Mm4lt+ zQZ^;3aSd0nT;nme%(xJj1$c|ItK(aGlPrfW*P63M7!q-*z4j~L!+XSvrwhrSk)`S` zkf$@4nTwFc!(KA4z(k`^)y^dl3-a{jTKpk2fMlSOAxu+xRP|0buIMeYGdFt@mRj&0ipFtl>fg1O+i#W+`*W%f5kyf;PUooms#aw zXBJ|P@78J>%;IpPzVnSP11TG?45XT`#P=-CQ^GO-nePe;1mRPjg)F)pS@4Mit{o^CqndC_W$`{t zSGzu%@Dx{DWL=?52Bbw%=h!qhaG&!zoF`A?)NYgct54xTq{%`PLng?AIJvf&X?Z%W zBEC9%^;{a}YrR=~Jf|x(Q??gq2uHqW%My%qvSG5bq-_+>8k7QuyEUG8OtM5@_ccsU zx_FFy-smMdaNnz$V1dgusBpGkUVd1>&mVJc-WEsTF5;t|S>&nTthbf;NjxBmBn3U) zFt_(HgWN9p@^m)JQHc$>3N1AWFr^;1nRo835IcoUxD1)Bm!Br73B-k1XrLaiyB#u@ z1TitHw18@5KtIjI7M$}j1q;NJ9FDTDrsT8r(qCc1JJgu!ETu-=N!MFz^j(KT+{=^^APtq}Z1+bJZgV(nF1 zjunn)jlU0yisU3vknqYWtZ0RaIB|~NBR@P}->1Z&oMwl1N4`p$9gvGyR*JVhj<=Ve zULs_%55W$OV)C2q`r009j)hG%cZhnb#W99zZvM;x?+?zY^Ys0=v{}54r=vTrjlqIu zyOo_bb4KEECl;zBI>QZ|^X{0RzjZ?~CAeRG*iJVAXUKjwrC<-%4&U1K76QYAmn5mKsO)MVp`mAv;rJs*ewaqz9vg3~;E7&V)SY{#z=wYsk`Uv6Ynk z*AfQFC_Y8*yYB4<8+A1BbiGG}=*h*)7oH7_WUkDJH{=l)>u)>QFR3aCQq)XKi$BLa z5{B>=%_ZCyv`l7>*lrL;+20*g)F~Oj)O``QsLJa!LENJMBPDq|TfIWc<*jkZ_;z!6 zDkm5f{f@)3gS8M1(mS7^Y>hiT5YcPQoV>akn>?6_S<_@$g=u~Q@B8{5albNqd!Cx; z7JN^r`y;M7hY3rS@~}b^3(mg8UfLTt%T8;_^;i=9s19Ds4@ZMCO*}K2&&3l4mNQA1k3nzJ`Z|$MrIqn zfT`#rRt7EQqXZN%pc`fcq@@!nQIAXuVOpaMe%uS}2*7@fdMF%>3Yp9xiQ9~CODQ1P z@Zou*Zd-w}9nE|M85R_&04T_*Py#BAjDXU2;VE;8R!NxyTHpJQP+gff_fr6Cba*11 zzg$lV@4CQNjrtn5MZ)DjD92e>#;K#&NG;u<&|P1%9+kL5186*_zNJTebz=emnWjTu zOco$k&V!=>j7((g29&!#0=Qi}6AGOhUMdJE#ef49O9L`?Wa^g&kp>XHF7PCqvgsTP+GFvBp(Y++xRA=g3>$ADEra`MoTik28nwFo!A zT-@U2#dze2_fr*Py`SF%OKt`~Ze-&?#i1+df{xy+_9wo{Dx{ z8Ho+vh1u{+P?Qf}UTDgAzEFp;J5&$@(+`$e4m#7Jm2UNC*kG+?Sh>4@$R{7zg-YTz zh7E?3vXS*BQ^AC}2+VsD4((L7Enx;M7$?YV9A=<$-_#22zNzZmw{nwgO5}R`2|NRKZ=d`eP+dt5+ynt@6){p*?zC&nFnVi2l0|LvN z&LUvw63Gkzcb-k|h?opS>N=j>DD*vS4p#~wmlBX&qCf$#tS3bPlTNb=*JK z6%eQ5ls%bFk4tR%PUNfr888fFzt^JoMnph3Ke>Yz?D(cKCw+E48V}U{&4aL@)s3S} zRSZJ)RauVm_!Q_wBnmpnv+foe=vrbQkHz`Cj`{STffAlUiQXJeg2!S!{Lea;z<-85 z?2+tCWgqp@)g5aDt(0vtoS7OE2Wb{ z#V`{BNn*@mC{BDGgT+7+8--(JzH=9P$YR*TGH?t!!PJcmv|!VVBiDZ!uso?^WL^x8 zz%Z)103Sn7iV=G;BUk6a&)|F>m$lM$Ukjl)6L_y^EG*HI+JUcDYF%ZM$nCtZ;7^Z^2Or{4N!p!U3 z7||TbRA1_04O0-B1}Vu+!xWno0_CK~Y}1xagVGSO&Gp)6iZ53LN% z2(U)3{uV%qb^)taQJkQ?ED`2D2G5hm_@pgJ_Hlxg_bhagaCFj~g-ewu3zmI7s3?G( zsu|$in_$J)>6n!q#>BpJmgr{=8n#sag|!^8ybo(bF)~q1MW)QtvO}maqfKxWZ@~&~ z*r%qeX>W?Hma=q>AcJz*%*Er%?G#IOY~pveN`U33wn`XxJO(%;3f*(6HdVp0p`8b2 zxtm$MKF0?Po$adoODe;~F78;Pim0FPi7MqfREVjoks#OCl04lU2fJrPD zqr?GF!M+J#O2P<$ih_usib4n-ex)7_p$cLMflBO$P(}V@uwnzqX%akZQ))v3SZGE9 zS87QDSZquNve+K=#7tc=3vc821)JxZgdCQ&oY@k?72A>m=9*%ZKJt_vrm^*Av5#)l zmlnMw$vF+Aq4em67v=dakyra>e>tg?pt%4{hXKA1Ox-%uLy2AplO>}PLX@ZkAXy?^ zk`@Bzm-Z|-;k*hBJHjKFo}@#hD3t>HxQDDZF%?5h)5s!fxk{YG`GW<|qw9{8OS<4U z9w#HXGE)vXmk>YS{wm6qb)S<>wBWE+dI9)fZvsCh^HTuTlIc}O2s2RAxk8jQqkOg? zRyq5f&j@s`f~~*4M>uSfPWWHVR1PXLdZDHEeq#PGUXJ|Jvdpz219ZFoACO4>*iddD z0TNd%hhWE$wZdwpY~j`ZB)-*@nR?X(mfWhy$hZz3-@*uDGnhYf!={BYCj6@hDFj4CP&q(twL>`@XrwOI-VFgc{sG-n_eg%!^JhYt~Jn@ZbpXhI9C55Q~HwWs=C#i9qRa zZn`Uy{QzDNFfn_FVKGI}TnnI?R!Y??lGVz8!;S1}btdq3sJT?1108OJ18r!AgRM41 z_E$HQz$7md0ocr{#Y@DPRRL&>Q~?^8gnt#0cuUU8kmr?^0i$Nt@qv0#brjqUs~Epw zR^X598rHEYiw?@uF3_7H9|JwnkZ%Ho=CZKdfXu@w#qnde49Z*$0jgnI;O^jv)j) z3Qg&c$k_9Ef!y(nCGXL6mu>sUf5+?6h=6p-HXi@AU#}*dh633dk2iQ2V9p_iO?Xoe zk8ClUW%scO%|v&q0o2HuY!8FcJPNakyHFTXWi*A1suWQ{EiDyUN_As|8L~@uR#SB$ z+SzLNmS6Vpf+-usCT!$q5GHP2PB1qJpk?O909y-({RM=xp}x`JgcvcPs)`}R7%=>d$4Cb z-fpLeVK#OhMJHk9=8r=tA2>R1JkLxV=G!*d2Kv=*f{Y~<2C7yC;~^?$i1#KdnQAHkt`%$)d(VYZA7mk3EG6=KymA7yz?!R>yexT`s(O^syl}8e zK7eu&Duqbg+rC-I2Dj4RYypi&xVC3iZ&x1hQ zYsQWOte7Yos%DRPxVrJ-=F9SOstlB;-(1t3LprUNEH8yN!@b_GmmQy6*{9~!EDzuE zZuR(f{jbOYyxvm22tNO|e|*!?dvpjC5sw5aJMHiROLka*Qxo~lHzQu}@2^X%I$eQ^ z88TOI6Zu&&&1obXn6OB&IDej>)x9@^XE9 z3qNp;EIV&YGL}uk$+I^Xg$n$>_qzhbs{2scPE{>{XGjFQs%`YJKr(Hl?R(%3nbP$? zLs-YV0{52G%Ciei&@X9bsf9 z2Pwn6F~}amDl`ujGZd#DV^z!pR@Y^*5GmzC9vK~PYmxV33Sh(g%^sUtgeKl%OH$U&sp6TNZeHR)X8&{*_NGH#zYw;rxBR%3 zQQY&FL{nrVMHu3xh*1&$`U6r&7-A8F_6 zaGvT&CVpNUoouClyuwuvJfbteP$L+vfp#LTbHI>}+a2)!#{^mN>PSv3K5Vv*pcP=C zg?J2svj{n!>Xa>blen=6TQxyWhc<}^I4wF8AT+l^G1jDNh>cZ)%7)wy$tuKYsA%&T zH^q>VYlDp@79YAl$k{@wofsfp?`QAu5dCt2$a6|HSYt6jw6)KbnS1vh6 zXvT#H^cW9m&e#jojAa|=YeXVSPsl8u&E&L@NnUiFMRylnV<{86!8@REkJIy`6Qq%T zxwuD0u`R`CZ}F;$WJw>5ebFuPrgLpRYp8Zm4w}Znk!POc#U1yhR(%Hfam_2UR_T@v z0`t2$Qv4yoaasHZYPG+on6uy3TbTNT(0Jr;80hVaXb;0-+q0exR6Lm?qz|ezxF|Wm z;hA$nFIfx{^!9eY+fLr@*7HB#uJK&x6f5K&&W-P${-xYRsiJ`uGTGnU-1N99IJ6Q? z6l?=!*%Ew_rat+x)>Ktd16$7)yVmx4&<47g7;yu5(Ibi9i~SOpua=eGQNR}+2O<_V zpJqc#7%wWUm+vc66LL^35F>bw$8zzy6+IBtk8lY5+tH48mmLlc^qzb6gw4m&Tsa0+P zMN&;P`S7g@XfU%h{wS_=UVhm1a3mS#lVy z0`55-Sci$v0T5U95(g{o6%VfK#@2*!O389?)qAkF-15iE7Cc`AW?2OPfGXH3C@Wi3 z!GH=!(9h7~n%$ZMA5_r@>2lbBk1-^1Ua`&LJ{0P6wMMYas2TOX+ug0_kBU`HVC4d! zW+w2W0JHzm|Mjo4yXoqYHN^f=LxO@;Div+2;o}ft*F73AN5n!bw@vRw{sJSXJcvU{ zzm4Qfdwz_}3jW0Q?3>N-X1--?z_aJThCImNj?(79e8vQG7I-UARO8yO_cSe*#e+Q{ zwtj3Fn0;;q7J524b0_cR-eI^N1`blUm!TXbHdlN_q3U1uV06HR zr@!i(Ony}3vb&|+JjdH^HKhz~11?Q<52LmzzlWv2Fr+IYpk0#(G(60^`~AlX64VCy z%q5-t1TRoMuo_&|FqosI#HZI~9muwdJ?PcbuL<6BLO)Mui`}P6^Rv`12c~9hTR-lY zg$iZ%z-^}UEB3Kg0NJJgZ3V!O0=e{g++*}K!OLYDTJ>8ouGX!PXJbY$I^ue|C7xXJ8c?yUB6uY5Qur?QZ%r{JXa1)JSY)uAV=-#*t+WP$o=g z2$dT$aW0k`xevFG`0(vFJY@UxcD>&``bX7RF)QH&Hqv%S?y1T|Ac{WO;8o)alu-)~ z%NXeFhxp~J);;=X(fHrd8}?5qi(wy>Y%fWD%$Wf_)joJwV004}m>T01Ej94C8}%9R zgS$Fc%W6I_wo>0)d!uQyOXQiIA1aN8Ap*Deq{SvPFi0pebO3K>5aI$A!1^0#suGHa zXWpMyc&}hh9*XzfaD6n73YV0YzPQR*A&@7bt@*?Dj%o2(e@P+24OI`FDjI@FG=m51 zh6(+6kUa4_IPtXZ3VjfNZNA3CymGiHXO`sk7>ullFj@jPB9xnk zSdTo)pbclF==o2)=Rba5^PeMbd&N0yY`%eFHQ1~xCu=W0;sJ59 z`;hOJl-UpI?rWyzd51BRvCeSF^irpflkIqqMf`9?qQs!YXcXG3Y~7 zz%A`l;F~o%zuOK7WIO!;GZ&kza{;diThXS-FbZHj!8=E_fr^ye(%D8ua7TT(o*0Rg z7*|8f+NBVB3Y)Kh+NfImB*C=wEcNDO$Sq7K)^Hrtp>?HpCN;d7Ww7CGEQ1a2$`}lr z?e%Ox6|NUMf*pWja?qMeGnJyS&bDX@C=%si?cn9iPa`q`U4e0qNX1h`ai=$RJ6@4~xvO1doxrj%F@7c+V?_uAOEYR4q@=B7pe21(11!*gIAQC5jV8Ia?^d_-*_khW$F&niT3*zDt4yPGLoEd%Vg z85cs2Hgdyp67*sW=ZF|?G7LInEKJx4q}~}YEforG=Me(gZZOJfE+vIzOt^ts)OxcW zTF6YF(KVo31e2ewp3%duHFjuR^99hu$sG6V?}pcFRmXIW&Zu;%YU@U}YXiZ&3Sqsw z>$avx+OaSx+L)Bp9GTh6lO%N)#5Co7RRvD$;t`NTTd!WWuY;Q}-S5PvMXT_t#m9FQ zE3l?R%pE^?s0MD#DcaJ45o*hJAa6J;452`P;RctMBD8Jtfql8r)i^Wl43D0TKHIwt{{B_sT1jP~?NA+PItWPnY|en*{Q3z-G><&{fi-_s0hL0DNo5pRV`FGui|V<5$LXg;T)L z=4B9002&r!132u3F@OVu+(4b+5mZ`?D=jgQbwx5jW@;K%YXkqwbRfXez+o%cR6pPb z>`YJ4?Di`ycRqtbr<|us)wehhJjQ_OX8>_iyTYjff?w@yH9*f_`Cbn#G}d8-pMxV! z%{#O-(+vGng<;Rd{j{%z%y~K0C7|l(d2fgTL$?uZ4%@p~sfi{ME|v`##jBN76<+aL zJ7U33FR<{4k1D*PTlISTK4=sdhT#|?xDJC9Iy&gVg7^4(v6^l_O%TbvTXxT}2u@cM zY;*_dhRjymujJ)es3L^4t1_e4r3JjA2!>wU?#TeRI%k7pSezRh|Ia{(5#=6xGV12j z*UR8xd<)j8S`ae2#a;&g7nkg$=N-bM|9(`SGw#301vBjAD|NK9{+9-CVj+FRS3N9d z^^ho<|JRcecs=|*GAp#GiriB?bhrB`&ZR-aoc|*{vlzs*(KK}T@%K{4uVH`Tw3v;% z^qP0HCxdZVS4`Kz7DK4m*S^aq_B6pNzwgBv-*;~H8ocO)>o%=}6GNKZTEABp8?Vs( zpXI8Imv($Ryjh|_xPEIlq)1PPqSVKmkYDG@QFA3`N^!2iV0=!|p$CUF)UjzAF}}hp zDXr>|!FoEW-KVxoEf#cl4QDKTC92TgEg`-f{f4V60sQxs9a@h6+ScB{hj*=huSx9d zxOwonF(EmWy9`%!TsG1J%C!m)9zA(TKmX+s*(E=IfB5J?Ql0&aY~Bt>j5N9v+O7fL zjoN&F)qqhNAXcVdOX&h~_*e64>MjMhPMsk_)2tbzG)#mM%J9q&dfL^)|1{?E9)?sZq&1bm+ZHbL2$#1MaYM0Z5e8Lx~+@HMhFIS;MG9lT()%8sE= z@R(^U+X!(K{7a>2W@4w5-!!+ITv0O5lZm8kB`>{n9`I!{?+?!l^|a>j&5LP(J4C^~ zYHz{l+t!1V%E6KH?-EToOwHpB|l%~cp6yvhthp2nty5Mp#@S*WZ||bugARCv3WT8YIeqUbmYsfV5|F>v{p{}+Y$l~-h z39==ROgz*~wRN{^ws_5SB}>o1v^iP^t>y(s>6z<(Lme@G?*6)S3g_;O$?@nPewsER z8(;e&epmJX7fNhHOW7tYryWz>|SPcf3nhOfQz|M5hld*=(BpSSeq z*VFJqgx^Db2I&Is=fm_AtZ9S%^<4LN>?1wqzMkv$-4{RC9h!xP_cGcY_QkeyA<-|k zn+wT)q0L-Kl=pIwvtRzMqQ(v?-YVcJMpBQ((WhYgMNH9`Y@cbm15yn=6_Zo_KcY#I{K z;!Y!9COe!A-j_9S4OhTD(bn6C9;5AYqlX{767E5V@4|c7;cPb^md=oBgUd>IINO7# ziKw5}4gu)E9qxea@ICebyuEQlSLp+?t@qvoq`}L)33T;(ZUSAsp_@S0?yD={dhT8i zIFwDsLyiywfi}v3KpO=>p!L>6Yw)Hvrs?Xya8ScZoABm4lkD2kK{@cDY*YrYb=F}f z&fH;eZPW|sU>aS+0G}BTk|U^QS+z`I%w)^FB!IjG40owac+HH`TrfKzwR259!AjQhmBzm-DGZ^w7y>8BY@kDC`-I`I1a@FY^tw`dRS7ZfmeSt@X#xs8g?k}nTI9yYi5FgG2snA)}-m{;O!=H4+OdBc=2JC zwcQ@v)@)*9_#q>-(!Y69pVmEK(=)Cfd??kahpeaL*1W=su1mn@8$4~?wt)?sq7W$1 zk)eIHGc5RZnc;Ui4~{ejPb}3oDCqV%MIp6LD+;A$&!xayY!(GRY%`O^1WUBpY!vv` zh6;2`8wGMm9seKp-aTrPEvpO6hG`WW1X~`WNS87|RY~V}tLom{%|o!Bx4-UsQIEb& zH{G96nUUX@UHQ5hnN{`O=GpR6M6pp&5p@_AGmH#6h%<~Xz;S$yI13d-SBu!pz>LEz zb#S><9JS~7JCBGHkr5|i@5np)2VbqI%#7IkclOzPpM9Qjj-~3N1WVba<>o2d_m;z} z9Y$NW`4;sgOW3Y{KNjI5j(s5~3SDJsYr3G#H(jN(=_2)sv@}X~8CsefH4N>Ih#IE$ z08q_Ie%5$YW5J6&?Jd2^n?S9)DuK5us!ZKQOJ$CB6}iH3Zpas??wS2YacIh$C$TLF zF(~i1zAbSTYfL3uEB(qOeTPYUoofHnTBlj$p=EwT{tno$_)rHQdhg&E+skbq4M)rQ z!3x`o&y&%~!E*g*Xg0eZzLYNCe*ezx2YAn*h4R`In{{W{Cp&8)M2)tbogOb2t#tR; zn2XDI{n>{7*f>$%^qnW0li{uDVqm`{&srECE|8xVn#1h+e6=}kp}Lt)HV>Ao4pc29 z?I8E2$0wUB%gqK`$j0?zC|hVc0h#7vmX5Gjdpt$#ncPBg6+6DC>lUKJ%xsJtPs=!P z3()mfw#lrK<|Z0EwoB2e90cSY3PV7BFMX1(v*rR8kj66=5XN(6j=6nRHb|#Ex*oxOBNLk7bcCmzAk~Nf#&SP=DtmXN;TAtrbHfM-u8uwy51T-zhlgJ@W+5l*ppu+6ZquBsi#c-QSb-^sB4s8g ziWl#kthof|B+WtK?0KVBYByS62Xt{OIJlNtG>6MuMom7HQ+BeeOTEpmo-E#W%aiVA ze+Sy3DUt)XHQfTjXnP~4V=FIGM4X6iZNVHa)>_Jm;*Gd6SywM>Z=&b850(r}?U|0X zTXV2@dmD3oNUA*&S*m!P_gJ8cMP^Ud$@^`(ZO(70y7{!F4TFAjIkmKHwFqO9sl40O zm=m^XGAHb$rJTA=Q8{5ZT@|g+_N;}pZMh3+J7q9rXv~3nEFa^j@Jd2BfRw$ybHfovAJXy5 zYn-Eeo!e-Z38!|Y$F=DiT}_6S*2z*MN=N!A!&A@X$ujPDJL+thr3}ea=l}ZTt=6Ksp}>Q&OB~OhVr%(n^!qo z6MkH$mb2$`*$m2cG!>gr;71tNpRkB7wh7m_tk%E!_ z`A!lc!K$X1JX~bk)oQukq+^cynPP}D>g!6Q8ThL8b2}Nx^=W2Suqs65ac$&y=uaCo zN!JMgTc(HtYl_*JLVjprqyOi^P#f3p+-QV?)U%}KKxpFKaBrn506IOmQ=t3cd8 zMVI?oGlB`3&WEu}?dr5FL+SBOV7z*gthtD@hVklh&hsy8p5Pph(cuC|F7Bq5SB^E$ z0%O)z!?|Z=MUKmP2@qIXZfDWQ(TBB#KEU5#IbAkGRosY@J)LewC+L7+h7v=@6|3c$ zVX<|`yEl;}+s(2z_Y&m4{5&xBB3tYf^YC%(A=+!> z-5@c|cyG6owfD<{*n4%AcG}+Dvn|3}W=xu2y>|J*<#KA+Y{V4a@?Eo6X-R!J`Nd4Y z51%WB`bq>Q(|Oy!^eAHkpn=WQDfMiKPQIwr6NJ&U0X~S9-*(;cma&(V;G367Goj-ao%n9r&vUTO-IADTy3ah$ea zi&%b)&Ia3A?CGAQqsKR=2obxQDbbiiLmN?&*(grHWZsr?N_Zz5c`^WLAu%0=CL!j@ z>7x`Q$#p*9(^#ilOmH?K;6%Z6jmod-`2r`mnxa;rKzr}TayGtKXC$9;jrIMbb-F@d zzk+FR`t^oQ5}~6R%Pu@RT`$;pDFckKn0j$}$K3{0z~@no zt~zR=x>u*uX~y_?b(Un!j=4_f%O~m0PHB~BYg07lsN~3bL^HkHj=L#`2J>E>eG6R~ zn!D+Gc4L+tS2OFzny|hY?~A0Ssx7BA&5{`os;|ghK}V-EPVmljy_t@Z*_E9rG*8#Z zX_F~1zA-w9x_$OESv7-K#58jVcR4Z;m)5p*Dm2^03C@NuE2n~Qyoq^=VaYN3bxcb*Z9@cbsO!n^-wo(QW>u z>XGijeYkgp#nh+|AKZ9r<^BR4liH$!#vEpo+*o8;(U>MN4>OEr+^(rX*!ey;ta)(H zgj6&<&BCH8sDFAZd4|AJk&~~ou{nvhWwN4oS9MYlpe+_BFe9SV$!R5tj02^(eW7x{ zrd=BJKFYbsA6zIp0A&i(2CAfh!Fi4#vSpONrcvfPd)}HB`*qZWq2k$&s}ziZQw*^V z=`O0Ul68mE++sr$Ug;KiDa9+geACo07hOJT9(g7Dq)dOt=4rSl@N7CtgUpm}=D7&& zhT_kY$LSrL1up>_L0`H}w>}DC`UXwkt*1hZ5_P{1usaW@3$!|YU9|HQt(i>%5}#BR z+H&Kq_9lX`^RR=?0_Yioe3fc&u0Lhxsgh?CKewyu-P(>8>aUu1lP$-l=ZPk&???>i z$uqqBkJ}qGV|QP+aML;UlF7#!YIc`La16%p!w~9aL-6+bDAN)AJ>AyS>KYwMJ4@-wf92 z=J1-^DLg{#I{6^w&~GDVL58D4J5ZrP!xhFIC|k2rBz*&Y05tJB<%Y`aMEG)Y+KCRE zwwcvjorqnlzRC7^mNFQByJ%XB3$q85_c7DcsL#}nn;u@vNM>SLQ{olUsFPM`P4(Y| zOaB?Ro5<+91D!Qs2g1TI9f))J;_hCM#oWE1YIWj*G`DD@EeqNfCoTx%PF&Cxy6TX6 zixcOltxg8*(J;(TPnwbNz`XHMg=}XiSyeH_Q?>Bl`f|o<&dD zrI8oVc5zHk-l=31#9c~9LESFMt#tk8MgS3CF3%*XLq>1;2!&jrKz!|9Qg#S0v_PSQOv^4n=6>D^=FblxF79>lqsXS zD3f+lh?UpmxX}RvSVP!VTuah??Bis^m}=`hJQ2O-c)e&0rp_sPZO;j>?;Q1 zo9Jq~jqOZu(srT>GBnZ7f#Mn)Jim`P5`n94*nds5n4v_xOOg%^v1?H+OfWxv?Yu>qtA8q43&nLWJ~P zBnjy|i1X02b2|+x1XEa@=urWpN7u2Z=sSQd<3kpcM66z${mjy}^kjXS@wTLe>Q=*9 zTHDcS?U@F_$xJv#=bXyKOd*o{yf}2h4T)QDD-}4L{#>Wg<(*c8;p|%TgV?q*a!Dgu z&xyN~m7KUk2ZbU)ou^1)U5Wt%L!)KOWgQqqLU;$Ihf#0=CkLs?fk{(5vKSt z*u8JxMLu3Gk7w!dcsaumtT~ZzwO($PkG7NIVD@p!7wX2tN7F^JK1I9YWU$PJ*Po@M z?Z&Ka&B3wE3u);v-I^{5vmFcRkb{&92s7wpwdQ;I<&kDJ|HH-8HRm?F15C{8wt|GnT9T1vBGZPw^tw@b=csrTLd7A}Q7| zV}TmHJ2kKJ3eyc)?Q6`+q@ShZxUa|L1~blmQKgJ#wvf@j>M7^r+d*=!>)a!d@o8xe(`um_$WQbRUaCwzFj2 z+B&I?Lv@JuWyI8OT_RRf9pVkVFL4b!6s)H!yb_8R9X43*Bh+3U?rVX09j{8h7P$A* z^%J~i^IAX~)4w*rc+X|UJzO!2u?Kru@8zpiW*eP-KvuGSbe&su^3@Bo$Y(E7|2FfZaJ1`_ynn%%gjF4`z+6mG-9%mJidDsU)=*jSzgF>ZmEqN4&RxwcJWKC(H3` zp%WK4_Nf*VF!l+E*OIv{BE~zgklO<9#UIK6jlKgS~hh!@$Bk&;s8{ox=37`ZB?mq$tsJ5<2D{H7Q1Z# zverKs_U>S{p}27o41)!yf%e4Fj%)H9(|mBSSSKdV6e|Gz=qeV`xFIgAOR_to$@Nh{ z=#^1msA{w@AKHN@jD8%>xl!?ukyg`{<^YO?z(NESMuC8-RM$oBEr}i|5XT!xQSJ&9 ztoU^9CWa|MhE~OW)uKfS1>w6&;+()&13xkI^kR$x0ae}I8Mbmg;U-Sa?=6Q4 zWJe6%*o!DCpprP)2)(au6cC{Z9Q8-Hj=N3br5AB?q=4d1Xnn8>BnxC$$M+XVg%b$l z2KxGjHZ@ErikW8e!)?7K>WLN#X7+pU={|PmUjY)%zUk%w%d=R@o)JKd8hZ6LJcPbw z?GeaAzKa_{2V#)Muv%r#>} z7&9NL1$hl})l=z^sEg6_#m;jZ#j)8ZhAUj!*e5q1;@S(E8iXrvTi0dfo?MqHBEYd1 z%cs~&v~w5b81pLY(_zQe`ISQ8nChOJ zmd9s|Hj@q`R_(uo)myqEqx+Dr22CsBn%kJ+wCbDblXO__9DAyxt z^JsTi2_zHt*PAj(nElD*=fO0EyP~hW<ijS7lIs!x>m5WO{KDx<#A6d=9jLB^cV_Ee8Y=`E4ToJZ?n^%72RsptT{&grSC z0;jiB))Dq0R|ToJWF>uFId%5zVa|HM-f~v*y$?y7AhoihqlPvCH#m5Cx;eR;%x0ps zA+9}kyi~XJ+gA4F!zp9Da5kB5hBwSE2yHVJJ8X9I1hQdyM9fuoIBkM#kc-%Cf@G5> zh(mX9c>Q{eW^(&dyB%=@?zb;-EP14AYvXNqsvQKI>#NHd56fw;RCoB^4^iy^b~FI) z@EP}EyVQ(Uzvsf|zZXMgW?${)!Djh5UHB7_x(EZ-Z1~dhDhkEyYJxMJIx|hP%lEGy z9^PC&jTfI>ev_2rYJK~*%!S#G>kEtrwzb5|TsWU;JLSqR?^InD*fl~Nub0s`{2Li@ zx@Wi2XB6~sktLJ#zPD)mwjzcr$m%VeW+q_IhirMKI5vFAk2%K?Xm)zMb+eK@=Z#o6 z?0nkx)>8wDdrQ2NUpA({;&EkarJ%!=KD(W!t$J;q zF@vPGjeEDgdmxF+(Yj8gK{HkiEqB#Chw+7x4Kh?w+9u80VepzLVoG`&F|(i31wewP zN7+=J+mkulIGsF6fX5GM@i7{(@U#3>}_?}QPcA8Po-B!|LDYqBO zT5jkZXNXRe+)n4Ml}{{b9kWZB`qmqpgEU${JM5D%nB!i8Y}thv)$-vswTwyK$h%$4 z?NM>9DsO}k1E2$i)0R5Undiywa1En_gN=NbI_asJ+Vd)|7X(~p{J_45t>ZF*S(3XT zMZ+82xUQnlDzDBKFG_RD?$tED3bjs9$EMRz$ShV-$Q;#?*)eT+^&}lVKE&B=Q|vhQ zYKl|kntSS&!z_akaTq?kCqcAFUypx(*0F!-_L01ozOu!>kmK9S4VDYZ^TYJon;gxu zTHbi9u-th+qg}|H)VkAC-jnan?ZoD;?TF&H( z=HUV@z3I4(tx?E#PmkXn-?X$%Lv3Tlmfy8+ysvd=?pQu^0=;S1V4%3eR75Pb_TkGbPf0QK&{I_Yy8<-w`EV< zSpK^?vK#AIVS{ZRLGvWNnJ$jwYv*E#Vz{~8H|muH)~;Hfpx>UI+~1ebT|#@Kg>FnW zyE9ws8s2!nurb~Cu5i>&?JvMIrrg>stKqzEh8(*_=UeGI{yMSE+EvUOMW!T zQkx&018*a1S5K`0=u$#z*sQ@^)3N4x_tLY=+$~g;_(s%yt0&aDV1`~qkKkNp?`jB+ z^zrcNw!RBD2kg)#Sd*-?8->(e1+D~1z6;PUeT9jtQZae+XsX}PK7x)?jp(h_X0v;B zDP&D4`lFV7$S@h|Uc72xyOpmRMrF-JXKCHJ-71geqTNO2$bQ!nghPe=0G`ueaNcNp zQc?DfjaI%Wxz0|KSuTM4-6i=3)~>DX#fs{L4=^X}IK?>%tM%+SsI z+fNy5Fq5A5ny)VHX=}}4cUJ`a+63MO!8Flg!&eoga*)UK>v#{__TXt2+fM!KlI^@o zBe$1VmS};-4tM9>ewwrGikdFpv*cBrb03dH%}KYt>=WZ`edVN?xx-dZ&CIO}Z<|?N zdkxy4)M~!wwtSG)v%p z{mx>f6yhbnfKuc5bi7WxKGo&GGW#4zRcN`*l&IKwEwA6Yn{l!ygqbZ;V(1*uWkcWFP*#9mC}-sLkK2U13TpG zX0vgz6X)!`iqj5?u0#beZdT5?ran`71=mxaGLEu&ljn{_N1rDLqX)<{qlf5UV0AGg zS7ErvgS5bza;6R$j$0Qr9docRuTpq!+@fT~L7H#Bf9JNYdy&qtVU3m@lYv-Z&xFqn zu^&ERlzDiNKHF$AP?3NzN(rFFyBx|>HQbCAfnWnkGQ&3HPqCs$DGEAlcw0Wc7^AKM z=bZ5{%L9U1fS1$cv6{JUL@X+M%Oq2T7o08IzsH$P+eMja*#25NNwzbs`|X80Z^9{a zfn7~D35Eg}$ENMk8^uL;B@T0=WbK!*t}1h-PhFEbe(BnZpyg+v6p#-34HU#{-rb8J()qDsb z^i7BWMA?kkMNl^-0;rzNiCQFO%^qNBniT%X0+w%nU-x_S?uiuCCRi zXo2NxS(FW@u^ser(_R<@mANlAlQ3IuYgfT=*%n^ln2CGaH_Rd$^Db8p{#<+au?vJO z=TMbfjU?mqOJcA+(Q?G5oHFZ%-?ioXK;+G`J|M0&poAz{K~U=Q;=_FaxNe{XBsGiE zW;uJ} zmy#<>h6Bm{pdU=XQtD+vNxPP=u+x{ zLbbGq)IIbIi{)*1Tbq?Kf-RdO3FxAvGMV_hz|CeZ=ZCwKc6%E<@Rv$lms@zJY9PdG zq1zfsyCfskZLU+McMsFO=qirQS`wM(TfMkuUk<9(ZyipT-0w@~E@??MjivJTt)`&z z)##@lrGv!c!lF-UU%Kg#tLUB%b+JU9s|)+cm8x#Y%afD6M>(xRo8smyKB>tTfLf+%I0U!yE)um%B{nAEtIQGN3Pu5VS9$rCY;woJAKmHkA{>kxZBaw zl<<0#*Fw?yl;mpm1Ju3BXD!HUp&4~Ta>cM$;NBKGH^c@D(neW{|0U@NqU{sr>FZ`M zN+R@{8RXmpHmg>s!Eq`S;5adVE>*qb)G5s|??YYMVRt8jXV>*=9f;Wp3>}fFa3I(A zgzR#BmElcKj0YaUZxq%K*~Ko9vKPp-hj@W^EgZZrn6cHa)b&EKb^aG3^m4)w$h%UX z?05{FJJv~Kk>sH{#M1hA8n}qtPQHEf@n8tXw2pfej}pJ4e3bab1!OYC(sp$T3AhzS zq$IhnjFcdH6_OH?_i$+`sRhv$lL(Z&oRq+{1!ZbaeKuIPRc|2fig5=EIcD+gK6b}E zCZW%&i>%Fpt}PSjKv$`i0mP-2jY5>$JPQEm;bl2Yk208J^eTj=+ikL@vrCCz5lc(_ z?I)$}x~3cD@L9NWs$JY%kwm+|xdLOYB#mk%5S@>dAok}X+BjVj$AjofP-Ra9RSS6s zL*cqR812XC*17Kv%%fAqJ6Ag>S5A34(t5huETj9Eb`r@OOzOuwQhA^~S-yu~W1SGU zgHWiNO<-NHbesxbbDX*W>6mqK#4-2KwC&D9W8U~uFLEMycAf7K#7qk6P3PH__Ves| zBL)bn)?lauy*GF%db@-N@`ly;MHNHm9Bx)j=NbjG!Z8YFom&+AI)fJcz06s_XpOmx zpfja!2r9-)J*&cuo>gbWf?L~oEfBQr)&fD-WcBgbSkX>QKkX>iU5J6|m5TTbrLm;hD>o9bN zb@RG9vlh$>qZZ6MlNS6sgBJX~%vr!_jk$}UGo_t~)>$&=RTwho)!8v;*O@VA?`6dt zMQg+z0F4dr&XQ4jvgCoeoF`ZBTw9ML>qb;D6tBHiAvCYQRWUe)+%F?sXVV|0$4!b?Dzcu3EWouru=5 z!L5EK_A9;3nxy?e*`*i#u=m!2$8|6*!!_E1(WQ11e3VfIpL zor?BWbS>UK%CFOvVyM0nO)SuUicElYsM@P0kj*t<_EBoeQ0g+{VfvLAkJ7Wec$i+L z#e?)ND;{V+EqagEx14w!?cU%u-e`w$d%KVwQ0xt}pZeFU$)okGf(y7Fdx+PlJ|C~! zlYC{oefgTpy}q;K)yF2t9#H!CzPv#FtB4Yku7cnWDxJ<>+Fg1=g(_K#{c@B5uJfwo zUb(d-8yuDL1;djhI~hh!p4!cMnLm9+#?5_?Za$|JoyuAD*`O^)(K$jMvU(JMUtueqArPTQV;tq7kVE4T z0lUs6b^tU^5rF6nYMT3cV>)(KsrI~m%Cy7K7|^lmj8|gT7|sLqDa#H+XL!>=(;MEg zs|@dX`xxF~XbkUIb%rl7YYguJ`WW6}=nQY?p!Jsb{3_Gu%zbR{K{UqqygKWbxi#kZ z2s+!jUY6c?Ie^M~1$Q6wbR=d&JV1H&{4R>`X9&kSYoN5f?;V*`3vg-#QiY#gY5D}A9W>X&?XPW3Oxux0 zHR@iYX&%6r9E%4HcI44T+6EX+ysd%SoJhw)c0u}t?s_!&vZorexyC|&XY94Ct4=VS z$d)|op?3yHeG?{7xdrMQNgT?aB8f%o82~Iu{{UcddKcUfrf0!*PXu&})MDWL_B??9Lk)ZbScfcGowUeL?{ z_kNI6uXB{jYp%Aw5v~BraLnWN#bzx&b|DD80a|dkN5d`$bYJ$O|DJI-m&uHL??s|L zmZRPgH_x6$>KShfv^_=JGGNbGn}gj;d#s+TFPZyl`2{j&#eocaal7{V1;?-~$2flQ zU`6w7(r26Dtz?~@Bs1r-!}eDo+c6wuEKan8%&lB8kbJFHj%ISiz52;vMjbmKbrz@n-A)-)^!$i+C{vmCQ_;)o=_YLp}1((^WsJn#`10pVRMKgK9mDyr^S^uDCSnj!=G~YO>#VZ=ueIk7B{eVh|4u06^v%x zaF<21)A?#Py#8#pTyM+_N3qe}ok=Hos{m{eC|U1sI8h^Eo~6gh=+uSjm)AV-Ydy5S z(mC_B7*-Uq0-Pj!%7|Iu*`W@~T_ChY$o$R=5~VtCFy+Uk{?3aE0LQEIsGG*JHXZ2r*TcnRd2VRVA>~HqOoa43+5{%yEWfk zfpTy3n>N$1bC1V)K%S&wKQ}Z(OWm`vz}dA%bhGibman~GQVt^JRBtXvk0owj$1RoM zI4z82G{>^oJ^R6mUR#Joo3t3y*@}T7sBA)+AUx=_4}Xvm=RoqDG-F7uvWs-T28 zr2*_WaTsfN!5h?W&mPyXT@S zU4XOa3(j*x zw0*j>&eQdA>c{GC@rQu7Np&XQ0_XEZh<32~-qca2+7;G}V&eYmF4Z`Tt>b1(1d z;XCY7D^oA^AVMqEg9t5D4AUC+@&%{J4KT8f8RdNn~$ zek@Z#*&({8o}WXh*1`_LwP`j=*5eMtx@9W{4kBy@mFzF%Eh|pQTq+JiUS{{D^>Vw? z@^v?|)sH4IryUI|qrBnMwxlY|np&m~LRiPtK?rM@ItXF?VjyHAI)Tw;jG@hCoI#S^ zoij|XJ6F(#xIdeYQmvD@s;q*c@wEt&(B&dXLeGmJ2^}y*lC^StbHlYYjHbKWxfI$B zuPpU)cvk>8@$XCyC)mKayWl?Cmoe9~80#`$s$LYb2Q_MMwRg{Xjmo<-?`F9>Z#BEZ zignl3ga9=e7b5KDUI>skDvZ5_%t83pu!n(M!=E=846`<%o0_tMpb6)VIKp7wh$9T; zjX1(UUf^^uSXIa9&bi|g-C4`7EQ|V8fo0q~Ld)2b%vF}Myk1!@XWcn$4rM$pUos|| zfgHD&?@rf(iLF9FDW^~I?q;pghlnjuiHOh3XabE!N+Dw)r;yXPLuf-GtIs6g;>36a zo7tMIs@Q$nsw#|m6*2}27IJz?jedl@MIq5}2Qq8ez2Jd;)Z*(gC20+C4T|A(K;qzg}jLtlF0uqos`QdP0CgXD2-NDK{h=pZ;`4f?1V{*7)#0#aTbyaLry5GkhLVO zkk_{YtzCr7MP>t!mxf83vI?L`UsYIb%0kwXvW2{byhhPN?i~*i@|Psn2vCS0SW75G zmMZtneo3N0{sZ7X~OD7F;fs9htJWWxw3KA6Yg?bS&=3)vXO(3X{GZaSLoZzmPZ2XyD}1$Y|14@#m?lN~cLz$Qi0x$eIglR4in!I);!v6kC&gE_+}mp$I_^ z^dOXjt$uk#lyW(ZN!bbkrIF3*$EGLcEm9RlsW3?qV@Ww8&O%aQpb2FavX-P3@)q(2 zPAd&?)~G6gB7IeHvndN%OZpP>7V>Js%#M+Tsh1?z2vCS0SW75GmvI?^4NqLJ@MPVmQQp8wNj)=36R2XtXS%s`6X@$JL6=>}uWG*rrc)T=B z+LToQMf$43YEu@nmXs~zE#x(d7IN=+h>*V|xki9O{J>g5A;L`PQ7Fm%$@Ez|mQN$g z`JCe9jG>s)kY^R;)0A?DDU0G)Iav{FMNJ~!P+nmWDoZS6uE;Fp4y6v9SsDSokyQ|( z463r|Qx`H8crBb3I&a#F?tbwq?cvKQs$Xgaz$Q+0qIIuJhI^(Jk0tr;*&!sKoEh|mP z9EhxmKD)*ire79bBSMgaz-U4-O8L;k7+Ge^M6lI`OIgO`wI<`rrISXkKt?HRo~9`J z1PO}xLcNF>b1{W6ClFM~8Hy@o_1)zgH2Ox71;N@p&P4k=wxKr2*5rgbJWYUsYUf%0kwXwuQWfyhhPN?j08q@|Psn2vCS0 zSW75Gm+#}uSArJa}Ds^nTWSsv~CIB!;R#$ww$ed z)^Zl*0Ri>=#uEiLbX&_x&TukE!iX&5IqpnMgzB!Nf>CV9%27bvMb?}zbt~PREXP@Q z8SH6+k^>}Lk~SuKOLqaf!0C^;sw&1vCpg}!i*$lF*So+8!sKL{&c?>>-Su4wjKX^D zcKY<{a`7ZxqvhQ_%|-eYZ|RwXq;=e`d?=XDo_~aWF>}Uq!@E~ZmSrAa!FXxeAH$u` zdG|_pHSXlaVZ0>Rp>_uY7VRN{YU{%2}f|OwN<|)DG%~J=XFi#0aW1bR#)I8Z{jpljZ!n1>-GR_W)$~H|XGSlp! z2raY2FEz}g(@mQ7wM!L5WtJ+4$|?;YGNV*Mgf^+N%S^IJpK?F@+$55&QyMZVJE9c~ zl_@J2DofU5$P8J*5ZbZAEi>cU^Yb*zl4A^-xxoyjJ$7NH+3n@xt~+9Oola)yXfwnD zhzHS5x+6R=2f=zSH^|UoaIlvBi9L{Ac!XP>YE*8vs~VN3HNwc8Z5Kx9Z@U08mqX;Z zi9>R8hm%B(S;*MYQ)^wqc@zcdk-o|uDtS=(LOq7e5h@r$FQ{$HZY? zxFw0t1HO(yVQn3?zR4gF8#R6@byX}WL#X0NErcwFlp%!iqs%Og9pKJOfG~7~tZz#M zleflld8)#o6Q(MRIU(!SS(*r@0?Q&RLQR-93~X97!jhJ2Ot@U5EW0AagtDt5%yuzP z*4ariyR+I%my2w8m|aUJ$#y0RwYO~FhX;I~8|IJ_CDtSOUeEDzc|1#p$IDr=I36z7 z$2LYi+D;CtEtwL~+*Ujf7mpXqr;95b`{*y!^=F%OkwIB%C@uu+#oSjf6u@EJpDjhx zwI%%?r<=X#*7=}XCyw7XR`#Ks+{Su;vFnX=K-V#_V+)h8#wi%DrP*jbU2T@4P@0RY zOWl_Y%Pp<&Dv7(UrNk*J2#4(D@Vt51+Kehn21%x4alKFvlgrd2Uk^@K>0azROI|%a zTue98AW+F#2xHbm36@o!J>LcS$dp|PFXl_Cv$-q zmha{=8IxCU}^Q)6XwQxIhO*hW~i)Ama}oX z&ZFY+ZnDP6$Bn6E(}4(3_iWXI94^M`vz{3DPZyh{FNn<1YV#uZ*IJmv>~3MVo~m)( zu%0N=s8*Y;tI2vi#mjKh&1p|@-Z5Q_dqQ9ogRx-`M&A?612ho!APw?$7jX+~s0Gb} zT5|!(I!`itM|%2nx$dj-!z_iRIdB^eK`#Q*~A z)H62#5%x=JWr6RAM}2C6_KL!Wz&>(^@$*fBntgG6XOS)^Jyl+yxuKArc(UeBt=9J= zh-t;jn$)$@7#!2hMdi_5Ikqv(W+T-s+O71LX zr^6ele+YWz^lq{_=`PX^NYQh-aw-e{C8&GpF=9}_8D zcxfpMDs>n|eC@8o3J$7@OtRjg%B~yBsiN#7BfZqw%Oi%0LF%dogfpR4cC<+KE+Cvi zuBI)T_O3FECcdl8;@Rse0ET##(bq+j;maq}(TT8~T$KU}bkl-*8$yr0F~k$<)r^>6 zAbtbBin8MHuypB9^~G*k9Z})NJgs6@bZ%$$?{Bbf>fF%%k~RIXz@Y@f=DqVYDNw*h zVNhj?vt+pLkCIuk7WOL*r&&LsVdRK|hR-CZWp?`N$*vLGrQE&j8|^wS;Z8<{VY(-` zvTNzlJ`}A7DUy&=QsvLG$}?#B zG&#I6GsiFqOS|Jz<Nyc03aQos=#m$Dd=H4+VpA!3)k}m8ay|YjrUaK*Nkpd zAY`*16=rz@Gg)27`06Q#RAeI~SYTan%9K`O)u^llAX8Y0RjjUxy6LWNb(7iuQCCC`wp%r7v^A`a)f`(!8O6s@d*dh878pnCTG$2VN5r$ zB9!0kiCm-M9WQyFM=(>YxtjPc; z7&r~q%ic;>=Z3;T+bX+g(6-7f9JH;niw140%%VZtDzkXdw&G;YlCm}GbN9v#SGVVj zS%p&{`QV&+p+85_&44t3?PKh7bdru98~5&Bki)QfYno+P{Lzcq#5BPsq&4Xq2*Jjq zO%Q_3Mw=i6!MO=S5LBBW1Ti#(5Kd0)u=X*fNxP3B3jn1V3jn1N3jn1F3xGZbG)ffI zc%d0lS1Ai~vg%_zq-)rfrt|Dd!+Cb4**v?_Xr5hZGTEZb(AebcW3-%KX|%6qw0Yg3Z1?2AKwwk`O167v8bm_$FSgN8p6tm5p%mJ%N;&Fy@c7)>$ zWoGd>Lz!JT&QN9+jx&^*72^zL0Qusus@*h;!>Slipmaomh2ScfI2QXUqsy5YyOxZdd)6zWWpyE! zXwidbMDUZWbt|!H6O=4^2@-tqc~yd3hos4(b3@@f98yTESikXu2QO%h1*_Mq#l-p5{BsRNTX-;&L8 zJ{@8D+wq^nDuL;ydsY_iy$>wc=3%VZoK7YI__eIzSgo6BGeZo5Q3C(MMD-0o!6=1? z*m)b$$}$v46M5fM0&}@`YaFR+V3?hQ?xYwT!3ma*+{n-PGeCl&xXQ#eaqCwyycU>( znbR6M!?P@<`s)HGa8<5C>|V0xm~$e>@VV|hH3GX9ms!6hStfOHu)&g&FBqn;VE4_A zn{ehEfcnjB`ao{>_XhPHmZYhPayE}=eq zK7^SGHl7JN<=w3kw|SdP*2c=Emi59ACgv|?mxhr6HPN*7m*Z&d{E z6HwUKG@VRQJ+PcivUG!i54+vA#7WCX7;uAOOuNs~YsxlL(c2|eAm4%M-1WhjQ0X{@ zbHj&OdNsic=J9>urvKGm6tXqVM%a^6F?+g;r8?$e7e=EXZr$Mc&JAx#_qE6y8OFot zF2d=bDggUL5@&OJMA<>Zd=5BV8_^$4ypX?MD0$4rUeJdN({+CM;Kp0e4X>})vI7LF z00EjrEmaul?kNpD-l1ww+u((datHdH4zE$Te4k1;3M|rCeIFkp>QUPTHH*>qET#~% zNl5Wlq!BZVv08-UsFLXm??rV^It6Hxn6j5mBZ`v&HHjnNr#m8&9IQz!tfde;Mno_T zTtqbaKG$*Cmt;e=c9N5m2$H43EJF+3)g2CrE- zz6r6HfP0w9pqfR(TQz%xl7W zK#k%Ex0{X$B!p@bNijgv0CjFC#%e%vCmQX=mF(e2-J#+l$u{JVm``(NQ=@3yJ{Hp* z=D_yuIgLrDLyQV29Y$*a!%@`~PKMTiHI$w+6;YWKs!1gIu|W}`G+=d0Qk3IZm@+6D zQP=(WQ?C)Hc1YpqP!ph{<6fUx{oqTJXtFVrCKUNFM-!4}Tq1%cZ%anN)E)i^ro4mR z1SafOH^OMT!OdW@zH2j%sK?n1Bkd$ckcwy|sV3{owe-xgCb9PC?y??FlRVOHN(4vP zvuH$;_blp7CTqkui6cETrU^&fT5bZ8HgV%P(k5=bs!bzJ(EC1LL2ofLa7&IFC{)Oxf;*E>Ei~G7&Wv^dWrveTU!Fc-(TcmrfNc+;${U zysf^2(a%NC4Z&{~Vf9jmi35tQX?AjBfc(oDf(3g!K;o?yb`fMp(AD_6a828e1mNFC zd672BcI43QU$6tS(w(3+4_>&@hO#n-bZn-~EuQz_a?;|RAyeWcg{{mitxcS?eCG@Y zZrNq6Z=Q%=4JkonSOD9HKVN;?2gK|p%y+A&ct7yfbleBaoV!7QjrbKw`cdaMv?n;a zm`Mz;Y^Sqvy0)L493f{Nr>n#9vrN_`rJnL}UAeeL*W=2m849y~BIe!QkmaEP&pDY2 zl5f^Zg>b6u-l)lq;CpmVRDBOIM{H1us@s`^)`@vTc6@e@)gYi2t}2?iHyabI3^v`I zY#$9r%lW}7+0N!z!+)^M=Db#qCI?eDALoAKmFg?AxSCvtO^R<9016v+LC0 zxCchwh|*yc&Zm_E{pNcKx=*_FEw_E$LXGh5+4Rx1WRvs|;^wRKq$k9kEBEi*y#C<& zl~<>$i+ZfLr3tEyUe4(u`naga=MTkS1jrz(mP@un#LFFaBjO%Rhg#Fp0B~eD{%|go8a7-)E{aPjTQBJ zJA2-i7F6J)$Lb(i!qB-xMUZ}BvLugzQpd>>)ZX-rNO|Sa@|o2B6(zBKlpu^D#Zbe+Hg`;!>;&Ok}Q3=#wlV-;jb*8^$h_AqGtp!7<&l- z1ybMgZ;pTve?cN z)F#-#Nm_Jj@NOoXJ7(X7{h%!udqhLWCEZVcU!wliVLexygXvwe9L}CfR*2TWWI43G zlq`n@x4pd_YuaU1%>(ILy2$#J&O)Y_lT$@gu|_1{BiRqy)stj#%t1s2ob}8H3}9++ zFJWk1y#&(F&v&8JPF{lQT+Opf%P})8aP1EmV*GIUpc4( zsmnnC)TbN-FenFyide~}sM<7^S!)%`-OnHahT0YuqmLOZLN5z|8E@Vz8o7Dec>Sag zJL@WWRhy@do^u~=O=HDGC<>6N9XN#0x^aln&zX0S)Gi$&^>OSFN9o=b2=jBQ^0f+* z%HygSef`cATV2^hms{<*hF9ydhPj`w77%JDH4uHg(*P*l((~$quIKJycgn3Q20m@* zT@mj+x2p8y%$j23$A#1`UjS(RzCh^b{1qH^2`F&-6oUdqSr$qFx&l#x(3Of3gsy0m zAnq+^*!Ex3`!B&@bM>zONHcwJ3g=W1`El{w2${$0d z5RcGJTGGL4GT5-ZXCFm7Sfhnah_PT?YQ+ZG2(@?lw5u6>i792aZ>TSUH7@^J@r4@f zCcY@ng2GnX*kT9=(cy)`8kel)xQ%UYTB#plNyfS1J27c#k8Fs7Kj}u6pKx_nRV zer~l^V)wE@<67EUrd2X}oR0Al?eXTMV|H<9n>h?Kn%V8*-P2=k@gJo+d){i0ke!Lm zX*nlJc5;x6XX*OVMHqTK#l%uRF4geRteb9lX6B(|1h?#4LRY4XWPLg)p(LwSx){5| z7)nnH!SaQHbpUHDVyTA={rt*yMIrv4^$++?1)!7o$mu1+H74v>&d9I$MqsJLUDM`sUH#h#JqcWNm zqt>NRwHB`{jMEWD60j)Tid5%<_4GLHa$R5OBlMv=weL)X-#d2Gx-#J zN-m|87nM@#baFwPnkaX4!5e&Ehzn{3HXt8(9@$lX9bVvIO!IDBW>%!y#&V3s2kqRBh(P@0y}fPz3d>dBr=@#0i;fMRnMr z>hqNvXgkztuGTi^QPe6Y6e1_?G7m`?3Jp-wrCBUen_fBiQ~`=XHz6~cQ|n}87rB^$ z-dg@Jv%bxCZuqt&Yiy3i#l8&fc3cD^27sR&B$D_k*d@>N- zX~(|4Q7>^oU1M0^;BMum7d%LgTj`n#QXs}6#S1g0s%^{O+4J|2>_Si{=k)y24sSyf zs{Af~!WPdx)1;|1SCD6Szr|dpOPRM)+KBHa)Ae97OO8<@AK}DCjHztVZNNlo&0UQ9 z)ZBGlr(KZ4jLqR20QX#IN>>v=&(NNB2&CZPw;Njwx?Q)Yo%#!2OXCsG1_+KDwOz@^E7LHXDykig1t==v% zxui0)1~$WctY#aaT*|ZO^R&h`nLv6^1v_N3_H!H1;=qV2Aw> z(WlCddm*#=S;w0b1^LNVRNiC`d^{rlVWSq9T05Zh6cY8m*oNPznSl7 zv(J;o_~vvG8q|~+^BjfjhIN-6#8CaE%?Au4xxz@WM&AYQHCrAZtR5fZSf%AULkYth z9vhS{+dB%1W()arTIoaD&<0N6u5PGU|8_4hAW;VzHIH<$M9C!8FJQlC9o z-sPKG==qn$Kb$;RPboz@whvG>poEv-=qiLhV4hx1)}^|+cpT)(5S4_yf>suW;my5d z{QhmWNyqLU@_sL!FQ250&dGXof|o}Q&?_$nC@kBE4K;SevKz~Fh-V%=SZ9X6<92 zfYCDCd6KSY7z@J7aQ22dlk{+bw`j_UF4gkQ%P@Isqbz7g-FftW$U7J%tA%N94wviW zgX86axpDC15;f+!3pOD(Zk9{F6TvJ;7*YLiM;mUX`<~G7HK&5!S=$MbZt`N9aoax#fLO~zw-Z47|#f=3Xn%GmjifnX1m&B;x^xO8TV$}>jF zYAK5(Xtl<`pPl*2_YN;_tR>JXVdJlGCwBvLk0pY)C9mR4%+jr!$-?`mV?xc0Z`Ew$KHQTIpB@Ec*}Cbn$X;tEz9ql z6(PYy4A)%CNnLn0zxkhS5)`c{w07(*+t@I4rdZusuz_LIytZ_T#VMkj1n*!X#_=F| zJl&iOF}7^KTwStcV#6yb>o`BdS7v2I9 zeP-mnC&|BE&WvlJ4M6dVV!~ggvV1Ffc7K)nij|L1{C98Onl8AMBhN62D!2IC7d4Ml zxdq%El*%oVR8cCo><~>Z3X~Px!LO^!<$8>{kWHE$nO)a^rF1mZ!>?U^f-tvnZ4dgw z*HF{vJt#!s2g-1;*cVq*YW{!Zjd0AxcYe)BdnUrj$q5 zdbFkAvm%Ys{p-(A!{zPM4I8!9Car0+?=)u3c(HlpxbJRvTlZlH2Xcm;neb8GseG_L zMQh&(*ypI@cDljepzkFkr!rusKt`?buB#0mcf7fY}u> z%u1z2V*7hR6;V77Ix&{YNQ91$%P77*Ext~RuP7?DfTjs_Eq%0Y7{>?_v{cqZ7H}V{ z#-f3f-$GS|?`W&6H`fm5Gp{s^43N@QTb=ev0I{KdGif&}6SRmgU z=?Gb>%ZoAk;qz%0?TetpgHg{|7B=gc+-T~YG0h!xKQJVQK_b`Seho|mS(mdX>G;eD zulb(w9mcPWiMPhG*E21BELyRZ$fy0bAPQa!VGiJPI+&!qNMt$@`OPwPs`Bywv(|<<=KPz ziZQlw*9}LNVSKPeMT`<%;kd&HU;6|d6Cbq8czKVd0_ECtWn$l_HcQYQ;`0k} z5%Z;w8w{A%`|$fmJ^|OS9SnZW8wP`yKWQ-d-|+j_{<^_njnDHRZ|T1kzfW*|DQJG^ z*MJ7}KZ@V~0N;uC(+>A@UOyPz#Pz;U91Nb~^N-{D*S~%+_)cK`hF>)p48i{wfO(3~ z*W>eJ@cbj7y^Zffr2T{V{sw#!^Y`KV?|1n89s7F$*a@z$Lt3Q!27LcMT>Sno@%fvP z{}k6ZbBl_YGVN zT;G9r!h^U&KReDdCJ2If0({V4Rn z-~9V$$o~hN4!#5Yzrp3nKK~#H$kmXB%LxJ5orA{~fq~ z1lP~w`h-s&4Bmn(!SyY;{zqINgwDS7eS^Vw;ri$A7!3Yre7^282ZJv=f}dW8zRo~L zxZa5C9gWrJGr~kwE27}*s3+3z!2ZL|NbsN{8`vTD7`un&B zUxf0AYt0MK&fxkmuCG3CF+Kzw%eo?i+AXrq|b{6@EEI)_e|23|^f{STe-l3PI$VDfHv1E}{*ANUb?9$}>+c{>A3;9<*=Gy}KaT5Xaqq$n*bn~l zX}CTEY5h%n{^Yg6;Fod#8}a%5xGv)QY5e{t@F}MIiLmF}q32(K9=_+(2ZR5B@1F+U zeF@TgAMX8MxHtLK!Qk6)|1&-lc7pES13P~@W!QXm6aPUcA^8WYnxc6Bn$nO;Tz$Y>3yO++C%hd0|^WRSlTt5K1i}?L} z!AqZ@|FArM(rhqz6=@OUx8w7haD5l9PfqMT>fyirjnMTNJ~V;MslERnMz*XT9HPwQ zK6S}_{=hd|-}iP8eTQjO{fFf|=0o0ZSk~%%=jV{#2axa2gzjj!A3O_PpR@M(q)!_R z{x0sl0{NHt{_~K1271cz=kWa&uD^?XvE1Fl{jY-^-U0m_fR;2r`Fl}Eu24U}0{zbi zgFlJS@5Jwmxc&~VkHzmVS)<%6QD*V|XJ?l09zJh^{)4!P^H=fw*Mjb+K=VC#_D?YQ?xaLv9EJ1l36J&gU;@1-pC*T-{yzvcLuKmKOy3UP@2I>^jV#yrZ;xb^k8 zz7H3lF+HYBKGG3~e=KYKyo>U~^2YDKEY^1`}+?S_x}T>G4D2i9k#eF5c)pUfM5knI}Z2I77H_q%}o5@J;XKE~hw2cLfk zv6%7rCvcJOm+<+wasTMEP`r_6mf7#b^@4j&nvdZ3zr)4vjOicz4D?S>zJ3Aa;uC+{ zVDLL|u}%IL@SA1jUErk-nFlWy%b~9?iQ{9!*W*6rXZ=T>7lHjoT<=AB`=_|~K78kW zrpeF$2A}kE#%BJpT(a%)HTZmsE2B({=`y`gmrU3H5wERmGEeM7u^fAU@p2J|{YCl> z?ZEasWu@#a&wm!OP~M+*@=<2~roBlY%EdMfpLyB%`31P=W1w%h@;+@Ew!?WZ{QE{c z53zp+zZs)K8h&S;&rill*7daK*W&s=a8Z^wo+-!Md-(kUT;GX{d|tMXgS~$a_b%a+ zGE+9{i+IHK`C^_KZ~2=(LjUJ?`V4JFU-Gfk`;?cDpZ+iVZ8rTM!sjpIqK*0NP4Hjh z(T}}sv38E8+z1sm%G<}woRMqd;RhICEQPOu{?#gpv?3s+K@J-ysUqYaIx;8 zAA8>p`FxBpI&^W1agOngG558MQ;dT?Rxxj3oT6{DTrk$MPGWvoU(=q|vc-H=>puDk zd7A2bK4U%2bos|V0(BAg`+gYrSf{hx`LP_1-!QH-ez3e+ZW}L&$$H(FL)Q1?Wgm|` zAqHu`9rvkswgZSmT~vLCv6au*?%^l@7++Zy*w3PT#9|+oSWNpP_|Ej0zVCMo(B|-M z57Kx){!-lk050nKKbEb1T}pYGALfO12kRyJF#Vr-{Cf0hf62AQ&bt0B^}&4qCERD9 z*XN5EC-}u5H1-i1(eae1`SSmxJct;QDPBtt@}r@lt1${Y}U-+a2^d+Mn$> zmL1CB{gttR@=<2CM_9g?FSc=5PkK4)`jw_1ePaGed=iJ>e;VI^{pVwx4A(tercI8t zsUPzF4DOMRV@K3I^+Q{DIj-P7=_pH^94DY<+F_bZ$EQi%PzI*Q@i>mlaZK*tG2V|E<7VW=By)fpm z+^~*gIbi;nPnHY%3i}E4JJt(qhp}w2FTp+v`yb!_MK&KlhR@hIWhi@Wtn%%@W7m)V zDX?j?Psg)1yLnqPkJRrI@a#jb?*$Iak@tD(=)<_jG(Pc*t)Efv%;)Fh_X#dfPe1fN z`dzqB9*)QEL#|6_%41)@4BRlTe%6;*Te1z$SV+Av9{M_l_Ghf~_2X^OQ@&66o$w2M z=kM1-PUin7fgQHTyv^Q)wD;*xek|GhhL6uoFU)6Emz1#@)96oK;>Xt@5563le8PXJ zbNUMJGd}Z^v7d2<-}%Q_$~Z+E{25?`@h80u+v48m;`6(4`TUT_ms{q^$0d%%(jQ-U z1@${FmU&sd6t=^~P8g z(--wl{Znr0#n)Nn{U%_=^wBkTQ6B1nK0@5@2kzg)#Wa~-XlKTM(om)kB0s+jI{s!_ zpLG>B#PuQEr(MJN!8E=R&&cx^@kx5NmwelY?HsmiJg%Snxpbx+C-#wF#+Hx2W@X`g z1O1C~y$|2Rc?jm8?J+*%JOw{Fcj3q5>64!K4(|K;FwU1%=fpzXPdK!5SJk|IKluFo z2yw%7ICoN=Gx75zEKk*W7|sQ8erDhELB797`-T0z>DwU#(z}VgeE^@|hV;K1pFf1p z&wCr<5$;o;954E!mr$?Z+TtF4hkt(yzkd-I`wE``diH_u;FDt(e;J?bTU*MHe)Nxm z?~TCaZ0GI*E7)lYka;5vRudYEx3LNm*@M3_z|YM&*0wQ!^Huo58)$>v*fve z`{d!AYTH~b@w&v;rZUSp;&6_Da{eIFWgX2rhV}LR+g5(+f^`gI1ocK*#yQID;}h?F z82r8-@nc}sGC*G??Yn-bt(VzG^K~lkGY&A$G3L-t)EVP5>sH1^ih^a`Zs-xZ9cX^d~D)+1=dsa7uuvto9WV-a(&))P9!$g7}|)s_-g2Y z<4^qV=h;38AK-ccesV69&nP?BSI{>&_QQObnBtz-t$U$uiSOGf&%e{gd1Cl+qc`Fn z*Bx*Tf%jADhuGhRdrbR3maaIq=>3HC5%uvuK_;Jn%0?gZ`Qtn;^XBJqsUzx-<-o_^ za2}TY)ak94?N}^-(`TqNmN}-$^yzC%gK0Cp|5*CqoE6Im<2d61^}&mBhkuM6&yc4d z#>MA@cUirV<|TaRGv5D0`2G)Z{i!bl9xgto&gSn%n*zUo0oR-H-Rq9eSy!;!`E8>3N^)jXsQvYmX=oZ9%(!8GeW5 z$IJD{aG!i!!$Ugq&__bw=h__B>#X0IPAE6ir(Ic3lAl;yqr$m=Vo-MSuzXVg#Ah4H z#}+>Ke|}DjZ8Cqa`g!3|{j)LdpCV0`^?#1fKZom&;2!1SS_6KD>kTN|SB?<>&XnWS zkKi8j$#$$CccrYf9mg$*!!wF z`&=i(@n_QUk9lR;qHd{UuU9_f*c|iC@<;n{EShDKv5PU2bHHrlaGaaA>ryWF5yO8q z^1$^AY#(z>Jsb;Sxug$qjGKDq`X#Pw;`%19SLTiSuG4S0_KCXoYl;5<%3J7P-fqmt zUxxf2du;um`SX2)8@R`ENE+sge|7odecGC_>D$0V`k!&1TyMrTWd95o*OfJ`OXHff zCVZ}E(ApE(_ntM zUd*@C>3g5Iuzat_r;n9kUCucgror;Xyf6+hZ%l*bisg&*u=H8_FZ0PXUk2Wv#`Tj9 z|AYAa_qd36ZfWyLy05@@;(s?jN&hZa0xeKS^>JR+aeJ#9k$GbVa_ z|07^guOG!H*S&Np`i|uh^YJMhD`8$JkFWFJ4|#kZSr;--l#}vuJt3bP*~y4M zt~2B(*Btuwh2gqGmM3D+AE^uS{^%+E4D_Ct{B^v%f8$k)&$WkKlgRYBPVw!y^7V-% z#NW5z-jCxNfbQeI!tya~mI;pCv#$Kv?O^a6(tnDJviya z=9j;l^2jv2yqBL^-Fz!Pzww#n`v|^McKT9X8K92e0$Rox#sH4>c-vD(+Jd+&6Mla9 zAAvrUhw?S$mHMF!#Pn?+%Hv}kZAu@bjl1TPzCfP83ZJ1py)2>aXur|*8^0UvU0{DJKHvTI@V&1?p9tS66aPK|_o#E)?tAh5JH8h253>H>`2N%Q{OibP zsG~alP&dS)ZfG0MInxK9126AW&s?i==}ft9@oQArR$%*_c4wVNf21!k&e9KPbCz$G zYubSR!g_@3S6C0y$7oZQt)p*xZORvQ6WWXRqb*;HHSV+l?L^(t7W6gxkJlyh$+h|X zO&R#fI9&B9-uGp<$v0mM`B`TE9ps~&w|@`%WVrr1F8=p_zvG+a?_xRoW z1Lfvl)4CI>iIuK9rL}9@O=%o8L3m>XJOg@5NK$J&<3CM&1mQ2 zKFb_+Ouc`}dE4IiZ8N@S!uyxb6t-eNnQ<{J1LR{_W%*`Xn|<#tZOk79?i+F4!=-6o zQtq&AY5Ex`|BL~Q55CXL_h{%J{(T>o5$dIjjLdsqIcbloth5u~2RhhhYwTDtN@m=> zAzP0Q@Tz{g#{LTj$)hQ+;qkJKif;#ZAlojoWP%eMxB=JE@$$mO=g9O$$##R?Htarp z<1db-HCk;CaJ3}msE(5L{Ke-bv`5${V*5x*{(qwjg;66K2r>|_2*^$xubkvatJIJNaI%(C*#FHJ8b?SKPa-KU|!2(@K z7;H|gtvjU+OyXdqJeruzWNIUkM|7VT<7hY^2qo)i z#jOL=b)0to^wFbji*%#ooSmXqXhLlpPfz#gOx!NOA@_L}`NoySI#1uXzu^gHH8O7W~3l6IYCr}ItnC=Qh_ zp1?J-X+tI8b7^+li6ikGg!B$E(fy=*8zBVQcZIqI|rFQ_WFw+FW!#nLg`6mbPeV zp1SDVBGM?q?qqQqr?U%js!5V>S)t+760SFa#|$ z-`p!8b12Tady|PWWEzvETP1_o%+2*l1Ii)2dr&$;I?Z8zW zYP*@dHBP~(P3MSHq0=87BjTVy^Js_H<9IunbRa_;b~IaeBjkY{9VlJd?LfKSJXh+Y z)(yt^{GdYVOuLzRqt+psy=m~)x57VQWpDng&p3|=JIHXB!ssMM&|FR}08k?A zuabyc0JqT?Ej4#qO@~f>y7H3Xw#*)?QOB_E9n#;moq<&fWSrAwb5!O3QggRKS#S&UfNki5EFq*I)-7)iyGpk>0)f$)&$`$s?WH@VKiH^O6*F%Y#JY}2m@%4cK1liLWZu&boA4P z24+Q?6-NZR3u853uaaeUFrGGyvfvpl5MzvF&>~}QFeWI7%t%UO8fXPIKZBvSrU`t+ zH5HQvPjuOcl`>D#Y%Wl&6P`fRFnZt-cs6oeDQqJ*OKP4*0CG<##puOy@rZN#ToM?~ z1H$sthKd?FXs%SQBze2aamI~3d4sjf*6vh zE@6S@1pG*ABgF=DGtH$7{fp@EBcI2bID8(NS(USD%a}h-JyNa_Jx~&D~wPNFgl~qbJfd@Qlo%m2CcR$bcchM0;~Q+-B+eV1~s~ zX5=K+-9T}*#8|`CG{#bWh7DpIN zKQiqP85T>NQ77f_yvZ7-A5ZO1|MI1{@{`MsHa7l@X@I9Jdxv0TQ&ipz$;UZ&$J`9(v$ zTBd{qI6$yoH7Nc#{Bwf{WIh{+)~Bn@628liC`q<`w3(#`&z4O^(jpaPk+~R~+`(wN zIzg}>J!)wGIkLQQkj$n@c3}RGit2HjmNS-_)~+w8X4Ek*mK*ymnp}cb1+JsEe2~sG_gm4d{yLH!anVF=By+hk@@5TTbfr3ogC9WLC&4b_FsVH7_%0Fyrdt3oo{Me^o1nX}L!A+R!#@ zN=)i5E2+JFb_C@QW5Z)}=(PXRtpKKDo{F7aeBp%)pO3j%W`Yh!$g~E9VIa@PS!1+a{TFNd~(45qPjLzj8*Ma&x{@F>CD0k8W+j(K)71v= z(E2a+D)A2OscqFNp*>>E>c)U~VUu}7>$_Sq;WAiEbkJz+X8P{cL6JtgKC_eLg$oxC z>_;@aCCc{3-Y$8EP};~;7qs&3N|G%u9Pqz-`O9?Zsx}Uqlqrq-Hw6^D%`slCG4eP| z5F(cA9D8_ug*3s zajzDHQmq*Ns8>ytov&^u@xEq#ZEP7$qf?zM7uqP8V+*TXW=&<1(`i8RlM z@6b1oTRPHoY};M#Gn&gayaiV)&Fb+eyYS?~3+;qiJOm@QAfhwUwFUMPWv57202}gd zk*=O88tGQ)`~X)}7tdaJ?(+{;kFl78|J9S`f$k)Ic7Us%DlY@}i`&Mco)*iw4sc=g zZr0}anY&m>kGAI~rz zJ3T(Z68PXpJs;Ca#^ZJDy&9L-W`L$qQZk~A*04(0Wf3n!axS~MRFZBMnZV$4v|X}> z{*g&$+EfAZ>2!q|Xf70J&f#XXiW6+dZ3I}C6o-JRtuC6yAPMw2ZD`Hg%(SOsAX+qz z1uK!Q2AGmrU(iMrUH3wi=IsUi(-uKbmRYQjnu5{%@`X_uSDTR}HgiqF=t2d9x!YCmz~j7uaTN=-XAPIn<;V(6L|GlyxCDiSk7Qp`Y1 zC-FX-6%gB|U^bQG_0im%AgCVjzbMsu^!x#?dYZaspA&g>f?ZjXi_aa{FYz}l1UQjb zoFgcC+p=|bF#?ijsd0L=3$xQl^AvsrB+mvG&YoVl^xO-mk?a?-eRiR;?Obi8T{OPl z$}I?o7cPlq^NRMFb3SzOIe%mSh_Pvbh(TH@Fm0-kB++GAjg*vMt{2j*lErKAUuON+85;T$LLT zy|Qh3^(!EHoMIjV8w`!NO-mL7V(cC_Ms@W#MI(-V!&cj@*6sGIf*wH~O{J?=7T5i5 z*Kv^&syfs=t>RNT$uTz->5^H@8a!^=I3^Gr*{e^OZ{9S%PQ&Kb$!4>1gCyK$H5*N* zu_dpjJ&IK{Sfq?)GT2(NaV1-sGPX>wOvyDm2E9H*bIi&daN9VHbVn%6U=c=KF|!<@ znx|tdb!gl$)}+ezb1x;SnYPob%AmaILR{(rQ+2Y_8g_4t3!%-s6=+nUWL*=!0Sz0pe|C5S)ljrKJG z34t$=AWg+WfQU$w4kC~Up+%aAh=@`|Kt!5;6#*e2O+-Lcups-ED4i@CBnY~8^>gm^R%zQHOSat!IXR}ZdT8~ZOj`0zp4>RL81=t}FgtDgtC2i?YR z?HbttVp#>E#K#Bm9j;|R2tOREXjywwQwF#HJ_}hs&S_mqt65n)&f@X)ctFqyqHpgs{J!otlF!$_1DZHu^Yi^~9w5C<`+5HY* zc)8Qw)_?Pb@1$%t3z52iFlnxX@QVS(*ga#g)S_mocT<); z<+1Q`;k&)=vxG+m<6-@8n?85%!x!!|m;&Bk4tn)F?@f7!T|@gzPuJb+21|MFFGi#9 zf^d!E9y;jCpJq$!7P4?3IN!T9_fE=pvoZBi9=dj5QPMqpkg4Bn&yV4q9o-(OgDt6C zy&=3&`|gE4*T{`lorqsTIQW1CQ#1jGZ94X2%QjRCf6HUy(Ah^!oxH{5X#>}}t!)(s z*$9ITtT8Y#`Te@R^dJ^U zHa6W^>({K!4i>Bk+kcVI63stW>s3FN4>@$ok@F8(gaJ4Eh-s5&Oalu|M(LM=_yuqq z(!hW`n}LH|hb}bJ|JS#Eb0P0qH^8RLd*%Fnt*gvObmThl+E>w4Kl2ZntAJiN5c?F~ zoO!JE+wgB4x~Rde*IbFL!^0NB4kmpTf;dF=GCwu(8e(iT?&N%9-CwP?-MWpj&#AEH zzOI3AKYQ5f4C5WiY{7wx_TH=E*I4Vfylr~0_-fsTHNYrd>vsZ*rjKPnC4P)$VeZJ# zOgkR<8&i&$G5ZLw+D_W*z=QYOYtr;B$R#E<^b2`#VZ9sZUl6~ZO&r`nc!{vE^C zt1)tib&>yJu&yZ!8$N>dYDm@S`TLOYp&xI@WoTCWkI z^?bhJC0wui44U6TVsFpY7~qlf7ZON1V6tizG_4Xw83ccmrca$dW9rOlGpA3Txy7{2 zCT%if+CD6w*ka~q_uT}E&IWOQ`;(9~gWY0MB>ni-*BFzRf#- z3E#u2*0H9B3KzVij&txD;5GO%lV+mk7Y;g*bR^be4Cb|3r6i@-4E|M0sqcI(im5al z+;7T0hOFOF)_2%mq`>2<9dv`9Rg1nu=Hc549x~V-W~-LVzpZ&dtCl=;|AWXD8Z4_} z)w1mP%nz#RPMd?6u#MvQ8V=s8As1Jp`bHW3-5_-h43aO|D3yP#72zFvkWRVHFj@VS zTTLpjac=ZiB95%TCea9rz1y7Xug6yMYhtbM^0tM2J1DIEi}sz5>!7ji?Aq7*{5x7S z{S&Z%K!?m{8-~80@LKy9y^A@srZ=%pe;qNqXz%~++X-jwd$ulos2>c6AB@O-CoO+X zBWmtdvi7}O)L7QN_BB)$ICRnBhYYqguivX;U-nk!SPj057LnM^-R4pqZ%VM0jCum5 z>+EUjr2FJcB1l*rOn|YMi*DzZ@A9=FugYCcgZY}?z0XZn?nm=^c4wZVr}gMk-%hi> zp;f-TSB(uo-9Vhb`yf~8P2ZQiHO+B(ulm=&k??!aJ>QtdvDcsntmlaPn$+J}U5_z0 z$-7B2eK{G}H~p9E(Q+S38lYib_S8cFl5YTnlq66kH z%+DM0KHj5SwE6A7-m7*Xg7*w#K4oCQb~Wops7tFKT5bD5g zdQ5K!ZkmH9G{BT%_a7f2*pMbkiXo5ApEtZ2q)y`}!qyGDe)f^BIA8Z$Y-|m@J}vlm z=3;v9w`iZ$b;)-phrA!_O{97C4dO=wd$67YB5z{7NqAuO4eG%`^AFhoV({Hq4~opY zkB4s!M#0(=K=W57*M|nR@_NFp$&GZALX{u+Hje87h1FZq=H9c0*m!2WX6c%{HTS^1 zj$F-yx!~QAHCStF@H%Z+YwsC4{Z^X$vAf#pUf;FoVUzoN8ux-_y=Qq7cFjgH?ATaz zb>nUw@St9aDSI8f|6Cj@eM`R9Z3;EJ<(tZv*Tc{KGT05FUf*1!9d_3DzlpWM?QMv> zT6LfeKj(X(ZVephh7V9(;2`s``TShwdKm+&4)eWh$vfxS#(?dSHh7eupTAe`&)N`D zU0-4MHURlB^9GM-y%m^Mdy@ls{jAhAwY0_zinVubc+$@Mq(92cBAIE^HyBin_TD27 zleab?*KR{dilAw8_q_L@9aj0S9*tQGsq=PB`cBP#FK{`3;o+-?QPx%5G-Skk+SYq2 zz1};f*BEMZi}MX33eT@LRIeE*Z2F|?d&HFACad3*o?7;0_Dsr6w{Z6N-y0S7okDCfw}%ruXfLBnU*T zF(Ulm&$u8+%_oeOiDI55PV51JakI;_WOh2M7`So4*}}teR)}m-D;r~FxK?a)vEGSX zuU2TKLO3f6ZlH9PBB5kKFvNv63yjQe=V=nSp5Pu{J}d~VaO!lt(Yj$;y+VP=H<1MO zalxDVPLsHdK2vBI4JH%|>MppO4{?EW!F^o)hT}1gXE|u5rthH_^@!n#aiS2(f)`D6 zmyvsn{KB|qyXgL*M|(aObQE(M^*g@FKTg)gW{gEy=T zu6FW_nCBUUJ~lnU?aus5%wiMIG(|JYY;6i=E7Qr(4WazS zm{*J>64H%CoC!^$c;;-m4yn@}xbFO&LjnWfli;aPzGcn%Pzb;a0J`ExI~4}6hH`~9 zS2i9U6UmRPxw-M}^hkbT&Ap9p7e(@WYaVNSyDoCUjT|>~0L`gQXhuPOhv42Q`iAKn zm^>u%bCEkm{v`5($Waz zp^cVWd!CK@@Upcccx&*&oycvg7*^e z7Dxz~F`AV3Sjd(3>rJdC!3jy^T+xP{J;F*)BQ>#=IOMzY~mGor1Z3ss+LU|LV{b)V*FU@=h{-3-*-0599 zu#KFY%9U2WYwektJ0o-Vq-J)rLzA>a<8x}(4#7{}-k}H>@x-Psa{$VBtw6UXa>NSD znFG332yV=xgXCbK^FOkCxZAsQZg)G^<6x^bKyE0t;*6*t`-}ZAGaQ_Aal|LJ)k!3^A={m?_4k z%%ov4DVXBM1q!8ZnmJx%OT;+b%es=n#FL}7BN&X*UkoHBxkw_JtaEj!XCdKi=6hBx zRHW)E5l1y}*&7Ta{I9^2-O(mGhEB z)Nj%Hh`qdir`A_eH6y|vKQvouq5hqz{dr2!ymDEGQ03(eA!F{hC_q#am|5ZM23LaH zAwy}_D@uveQ9O{^`)S2xH>dW;T<|WPtu`NPv0ARUs#I!-{>M{K)=rD3t3%)g$DVP;L)t%7PdGV0ly8S}YAD|f$TD-pQfS%TY=SG1^Hg>6Rr4ju5Nj$uB010; zEv8eFwnV2wYE2z=rbT+gj@D^rri=nyU8NzWTXradIFs2lDwYbito+1cF@SO7wy-c% zrF7ZZ?7}37xM|>lA2uJ8q_msaT~a?e3QQ@JLXa{nYJo2mUB!<>oB1At34(o*Xfeum zEF-xpmPSPznKT)z#S(WtIzS1~c0gTc>D-O&=M;3GoErzx$348dqSiu1brYtBr5`$p zV!<{;J1R(7N=g93q|zlLT(2G0R9io#(kr36-1#`Y@p{&7vkF17UobOH%=^twh@ClR zN6tH#?Ky8}-p3i9#Ccn@4d<;f5weadSaDJWBlH1Dv2z2hPX0&Vv9qhAn1bEC)EY#% zGZqI!cZrD|Aag}iaAi~4BIxyWq~jYpm+ruYx&U86ZyYvP=8c*Q17FSz;%Jb<`Mf#^ zju%r0mdwn^9Ry^ZodE109Tyz06cCtdkW_;UF>EmInBsgb2G;|036dmuh&lbdFLxY< z(K0oTtk$~QpOe<$5!)KP#&H}b)cJJR1=l&Pdp8X|0R6wf@lOn58(^q#ULLgu*GI+R zo~Rm}gw}V8*Zh{rj!Q+B$>#oS{kgal2J1FQ!XgO6u_|>r&`!gIW24|9K|}mHgrv3Xh!F`}F}nmG@IY4WR)LN7-uTSF^`HBm_m{};3_SOj&ae9G^gUVi zj%uWtj&%Dw8m-LtIe_j8*RXg8*zrTL}JM|ui3p(=)3C1g5|49y`{#W)rQH^0u9n?1Z%INcVFO`>H{XDRLd5c;mFSq)${t|jM z*z>$pKQ?vdkM8d;jCN~J<(abi=KZXBmDksE{!|gr_pUxK9rj|I-q_ zCQiG);w~p;{R%C+mD2u%)PrbZuy>5$E~DBvc$ULvfMvBYdASq2F=$G+O63;h1)O^q zYq)Fksf;MIt~PMQeQ#Ukvv07$R0JEMbo2aUcK%^xUOd+YAFyb5=*8;O2tw76ty8Hx zFF~*!2y0b$gK`*)Ul9?f>MNR{n>PmG=QxT;MOQe^RK8}4#~Jw=uFl11B`_do4v})C zy8Y2o{HlBfe@9*4`v| z^0X5ORskwEHSQa?ScV)Wi>0b3tEb9QsB4AGja+7C2Y0~gfe#2m^E!bc)m&|et5!My zXPcX(Zcuez&Sr3nrSt6#q2Fe{p>2Yf8V#9?MXt=-skx+rtEH0k@eopN81 zZ9Mi{o)B3n;cDk>wL2AmCmL|S$6wd|x%`AWPxtl!uN`OX*G%TGLw_C}ELQ`)J?y{} zgM9RV8<}m@hr)Fs8Xj&B7{ELTj-%P`E+cmIO|L=^pODUZc&I{BigzWP2t*WC{DPIUyu2x1Vpr8~GHA$&_R3UJwr-1Rn3tB^G z`UO^&Bkh?t9WE&sFz;GiJ58cfij4P^RD63okYr9t8wV$#ayEF)EmrPCYSU7i%gUwZ zxF%{pTWOR#N=6&&$g0e{p-f&kGkDcSbwqhPlcj@$sgeKLQ81I5=@C9u-SGj7f_G;` zL9shH*G5U)WjmM>G0Jues8Cn$V)?xGH$5s2is<-n2u=-}mqJpG=LRnpls51VJ!8z# zMlG<+T;k+XXR@ehkB-Zy#Fj8j$^Y?!a$2;9y3jjnbFd%l^|CgzfL0^SaBvmdMX0Q5 zQJ^_mG|c>646~IX7Sr=ZPQ%LdSsEQf$p$ed#8 zSeXRl?L064)!G~qlF!< z7a;9A-NGDEH%%OD+Zz0D!;{1`)9JyzuC`yGsA4H%6XI| zt<_=ic$eokl^uhihnb6}tlh2IDc)bNor#0l-N6aCHWUIeB_O5>o%NuQvD(>U zu)|F39{QM2t_wyIL6>z~whog{?@n~*2-o_iX*w%AX$EnGUT#MKD}x{`+#;O!xdoK& za9D6H>MuTdhr?aGqtW4-hMm!x8>fbOHzJub@Hn6zVFv13nHl8Wdzk-A+kc!jbySj6=+u4@VA^) zkP{0@r^UoKMf<)mh4fQzS@U)|K=C-twVIj?wJynGoKPIH_c%H+k(2U5AgDg|>!2g|~w$gE;o+P0=H(WS4gAjn+EJ$)U~UC;KQ@SfnNFrjUY>)L)9 z*{L$Ja2~WR1-BvTaubQd6hZrmJHkzgueFtxS*d zDmE%I#4vPy6XxDfDI7-dH^-E?G_>u?SfvZ-zA#swoGVYwDz?=@7O1-=X{q>5;0J<{c(c?Xe{KuNJvuuKake+}v#2;8BI}O)7uWHcyCX z^EaXVTHAc+o!hKDmW23e|LBy|kIU`55ry05%4OR6y?QeCuDSM0^19U`XNMXLZo27~ zKs`zv72J?DIA9&HaBj^XB>6`l*Znw*cyw@P990o|ABP)!Og?BoB&~J<*GJ0X_6XcO5Op{toMrcc z@7@)I&N~u0Kn_G|To6NK55;zglcOD)+;=0z$UPuFbm1brM9PSdQ!V6{NSWMuiTPF{ z=O^addRCpV$?{(IRZmHvUnBqWU(QXGH|Zb$64fx#ZTuN8vwo6Ca;%jlUVm19YJ&s6 zZsa93?Q*6MKRzy&OCtF3@fZ|2KlXf>63lUJ2<8*ExmWdFaC;eCVw~k9%S?C|%0`ZZcb-#CT0eNMY*OKRQCF-SH6!-g5l^#4*wVW(N0ew^}bwR(7vB6u=^F4i(D-D z<2k1b+A zyanw)KyF}yD;AfFxfqKZ(`2C>U@NdpE3}SI&xEhxWKeAtVfJB_o$Z=?Atgj8L>As^ z_PPbF*kreuIQa4xf(P(LEAIo#DAxrXffUv|&^tIr&(fs0)$Yv5i0U!N04HH~7-zSe z2pvLsA@^gd9t@D@bug@KZWJ6N$yl|;4s-|D1MOHD(-!o`nQV$Bruulh?Et1|6W1u} zH8hMwf-j9?9Lfc(j8Q$Hfcg+3w782y#*R^k4zm-z&|)@7*ajpQ%ZS{}@wSc&mdI4H znNT@noVLZ!g!@M&w4b4lvdH<4TU5+nl}r2lOOML@CDPqxhX}_l(&dKq274;|0y6v+ z5l)#LW6j@6^7oQ`nH+{;JX0AnzuQ(*E%0#W?k7Wn2m?pa**%&Ad5aD+60C@;kZ{U@ zfL%`U&;{>yAqF>bm8nb7d6r1HncZAAGuz7!c6<49$CrqQG!7y?fFCSfw6i^JXE}Xm z`G0c89C>OETDOs(>?lXu>c{M4-|ZD5mx)|1a*3$=`#MC35Wsi<4p0iF6KWA9{T~b) zz7cmYs}V0g@TSslnQXOG13qEiu^9upLKNrAh2Rw_C2D>sIU&js4j5`i@C5L_fBKxX z`wY$@jr?Pokey~Hgd>1PjTqWwN^&{FPv3@Y0HvM*bfO$Ck=Diw)8KR{6>>Gqyd&jM zIZQrhx3nLob_>=3grOWtxq$d6;9?oB6k)NEm&nB6SqqaymNtxLgSHS%x*aSwOFu(0)4yRPFVuPx^Sj#Th5@&F{%q?8% zWSJZ5&*h~!UQdwg@fV~@aU|FQfv=c zlMZH#kc`bZjFu7tI5BBuY_&xb(v&@aS8%$jLmKn^DALz}&G~eXfLuOTNm5Y&mf*Oa z6V!sMo5hP1?+&i>#TzD)le$g_EzX@7dPMH&9pJ^uS@^IE9`W}~yfXYwo(=FVKFYNA zfFWH~L$#Wei@;#+#i;m5Yf#MC6=SNg9|t*VZgYLeSSJX-|MQO$q)++)&}sK++x1uBMokZ55|!!F7?0HAE&koM+*r zq$H)Vq7@Q%H$?RTPl#0?-lslr(Rq&dD@3g8OxJsG9yk+EF)=Ni4u z@2XnSrj@7_Vt;a^5QJ&tY#`1b_qh9^9!l_M~X}z zNIBR0>Fv#F#!&&^L!uIdyQLY2&|`4W22?=^97S5?_f|eklLRN>(E5a`7i8*nX}T#I zIrE1G@Lw>fOw=I9JaefX5u%a>8zT^vV7Vl})oueLGvP|R(-T%!+V}~IoFeVjA(cs_ zZpSc22*H?hY{g{f+cuK#l79W-W1c}UPW8cb$XMkxvHN5Yv>V8tjQb&n*+VUktJ8(E zoG#`tq8faQ{kP;Wb7-f(3LD?$P)Z!?2Gq!(p~w_m$3PjA{34`qUHkDRH-L1Q~G{{zvqlOZ1C3`76WTsW6Z6^5fer8?&} z*G-m17j!b*46F6P8ApPEG2wXGSSFeYW;A6~oK7-R8Gt4nJpiKinbP8GSx*$Z5eWEh{WtakiRIG?hsB1&l- z^D>0Pc{o&^y~L@n))y?Gi4H+T$?O)w7I^Jv57F>Nuw24#O6gQ7_nnKUBE)}#V5Xsm zZR!6kEKfC_Iv$#oOuAON+LlQ3g%O3zZSgy>Nv~^vFbHMAE!k#W2<1FKW)2T&&V z!S=Fy%1B94Jdw;~eRElnNAVr}L$74vq3Zn{@VHgvC(>Kf1uBhBUP)0_f>2!fX#q;% zsR^UHDPW*R{?h_R?^k2qgbSlFShvAY-eD~g1qn9_L)sRmVn|O5veIIM=cF9mE9OLT zr_j7~(%&wQ?+VHdodXX*GYC|9QyW7w>z2uu9ww>kUfIN{*_5u}HHipk&8E~pTf@B2&_@pQIVw{-(a2+Ln9P(?&$vDi1QTIyT|rj$y(6!pBQXWP3SWVw-xbAtG*kyj1UtBNmj2uML0k(bqlqrpo?zNsC> zy!@fJE($W;?5GqB#mtoL5fPiYE$zwp?lq;Y0>q4MCofVl!>9{*OVo26ZW7iFI6hBt z4my~ROT8&B=7Z)#6;x&gIu9=sgAMa$X#N$J)i!}~hLda!Rms{mk4g}XU7@)Av}(fW zD;3jQh|8g=S4=gol!!4F>%>kts1Uo2?FwV|~SV1?y-p%WzLa+6;-Jn$BA1xz8y`;rAhj z#ZPq;EJ~xII2=&Eps=d;PW#qZst%;@(^A;Vl&T00>?U@`>Wix=1qFnhA6vx?)!Hqz z@BC2+o{m6L#CjsESDZskBo# z*#EXTe_OHnHfpE?S{WHK(U5czhZwG2csKu8PV_Iu=qm}mg?QfixLx7|5d&~X0i0D6+fB~>!p$&5gtDD z2y^(x0fvSBgzYh(lReRM_JUV~(@KKxM&wh{BL@v77qZb?EB{YhoB(5}nqhsx{V$sAw^D zdO6ikunK2{Gu%&UPdC=w~Ab8485cCwz@nx_yK z#^rLn1p854r+!34qeW(-M5Hpz?v6@ID!A#e<3QEMkVgRyZEX+9J1xjkOLV-A(4ny% z{=8MF`5QT`7lRy3C(4B!(-Dt6ib!=P*Uaw0q*)1Tf+Jy_Xr~!a4=Ppa2Q@Qzo`-Zr z=P33HF>hubwm>b!sv$!v!AR8@5KygoIwVa8x7@kPydXs;A>>|!!9f#s5>|2uY^>uB zj|wTfd9Z@AlvA@lb49eLR;>fjVG4Tij$xr+QQ@gI@Kh4?j_5vyAQR!YL6c9ID(-pgHBjvo*^m#Nl)~>hF!{ zUx>zpOaOJM78#5_7#-Z_A7Bfitpm!W1)iyJ$CCoL3KOgwXUD^zzzqU#cQnF{>}Y_k@%=5C#$2j5 z@wCL9nb^NFtqVg?V^cj~;CjT^`2Atr?}0ZE!o0&iG`p#5XD9k}5W3N=NPh2;UY>u9 zy2TW3ML(KaencH9;?FfCZa7kUyN?(1bzvte@=v8JJ}I?EwcY-uA(Up z`V7?s)q}#*S?LiI*q!5PcC6B@)srI$U~2m#68>Hs*_huDFweOwxTlyD;dstXcR!B8 z8#RK2HkirNEOTKr&t>vr7H$T&tu^C=o2tMqkbfw6skp1U&F1P^fO9uG+NCp7m)=%p zMYByXwfF^1lIco@;Aya*7yTw(h2zD39d+QQ670Mv&lL&8oL7P?8Tn>O&L znVf*7=(V077VQm12+7%L-pQn$oubn>1K+y9QEzx1Kr9Y=@?E)+prPW>R54d5uzpe% zkiIn-)6^=Qs)WB|a4mi}_3q(XQTr1Oi~JhYI&?NNj-;E^DJAe{B6$ih!CA-Pf!CBA z^koRNqw(3VH!f5_wYWO;r4@a@LOWMg=jq^TNBXf+#ylZ7_ILN^=Zybw{yg#tl)9-~ z+%EPuk=w=GCh@RnczV0YZBp`cze|sjn%sBDJmfLM(Zn%w=X1s98eJq)d{p2~m7ZES}%P zGC46HH$TeE%~|y0%-qTgQ5C+M$k9fQF>-Yh{czxGuC;OnD{2S4W;vZeWy$PKB41&c zJ7dLtVZf`lz(Y`0n8HC1wJ%EEfm=bWFYask_?%oT_AEFF{~k@{@sw$Tms+E>=!c(> zui_WlQ=YKq6=5-Vw;_3(w{0z-kntpvZRVRbG(U|RQ{)Vh*|HT3NvV7-I93au%F52*xriTIDm*u1NJZ%|tPWl5buyErqmPC?zl#6ilr|#;TbR{s0@ocI(91 zf+lp}YGNQugy1!Lv9d+_&QVamN-<_RSrr$PvP3EMU|U9*StaadzPCkPRpONX*^rq? zgu_^xE36{lp9x9$m034B_-6~jdsgP4*EMt7WsO`o>_r2)@>fLFROvk$sq9Us(}*+Q(J?T)CP*MG4xum^VQM$ z)c%OHz{KE93TRB3RF6WELv+)iE+V&ZPB+|8gujX78y=* zy5dq~TM}_S(pXF|PJY`$`&YFIQ6eCqWd9qH|5N4r;WqPJo4n8lOCE(cb#~xp zD~2m{3+74(i5RIA4=apmnY1oNI=o8k1MLsv7IU$Je$`sFdAj*UYx0Rol?G0u50d7EZqsl0j%(mm0IJZOoQI!=n`Ab=@C!NClts1^o zWt?1s%DPMnw*@dymd(>;xfD@MQVft@QYDoFc%Id+o{XD1sDRc41`tT$7=>s1H*4-( z(iP;%vaBq_f5>lTfdC7E+qBB59WspxM}lM%tvArJPPh)VdW~atlI}CQP}g?V|3Yf4 zm*FLnV?r}m)2>_n9K#!c28`gOq|PiynnI`gc#Ep(Nr5~I*3(Q@#}H48qzc$JmHUfj z%Uta!2UFud*<^n&Tm#S0%cA&rVW8FJ%nx3;vJ(`aT$fYp^3A$EwT=WZ_MEyq7hMuZ z@riYFAy;tYM?2(PS0!WVadIC}uAIXv#y-gDcPCW@dNJ7Am8jYYrf@+|dCD4ewHTF9 z{niU$LgR3V-=D}6_&HWc8COIr1f6}+9jS7qOxphlKJ{Lg|8C}X;&~z=>Vx+#gAp_KR{XIwSJZ*r~`?A+lh|ZaSLr z`{G0j%WZN{{;lgDde0mr3p2fAj<<4%d@ec6O@nphL%joX)4>09Lb2m>|B|&ogRin` zNRfw*jfM&0GTd!+lNG=T;SAkJCaHw2HDoRKT5ByJlPeroB~@Zkz?EmgKoioJR!9mE zVp3Ysi++t}+Zf2*`VKbt!T;B$R!K^$x=3lU?2S`vg>+Ku{lU}JLV3=3&v25yt}M!B zMXtU@l5V4&mxvtld=59?FGv-0uybM;ptFD=QBlC+WTM@!u%+I6j(omL$c3G%~)psJf2(5)0 zmdP)y+`-jbl94i1^%%Oj(1Op0jfUipx5;BB+Cr`fBO9NoIn6nCk5WSZ z0DRE8ZtPy~hTD>n6=j4@Pu#A47aH}*qe1Ah5X+er#JEBEl!6jHIE9h0lD!q)T8X2Q z&kI>BvvqR%Wik_3WV1p>L);NZ>0*)PQk-Yzw#_rg8m?RCS^rrL&Wj?_dhA%hi+Er} z4{G?5#nBf>a%p6aE9kNibpu_4`lr-!6B1K?r=AD8AP)frc|T^Oa8@g z85fe;!7!i-`Bh|IjIhLz8LX~>jR;cW)_rn7qksK;cSKlz-qfhz+|TeGko-zoU~J94G1SO;>~sa4ctz$%ybTeSkQX$k;06u{2RYVA3<< zMCs#r2|`MtwmW&Z5^xidIE)ftS7mid;EuL(3SE!ay6g~aXyVCQ?0{7c=8>-I>N zV9}sFG24F}$*mDP(#WN$yG4~zBY!gTyx}gwdRI9!mhZ&w-x2&);J5#Z%&9R>-E(5o zW@g9_qJ~_1F*2Lr&-+&_Z{WXmx1`}u$sd%D$&Lu)lLYVSDA`JWl`1-()|a?sGwsEb zF>{WvI}Vpt*TKnMb$HcMSg7MdUO$baAsytL!d!o#<7C$0anp6$8{Cu{Z{U^?w3~XQ=a26pc{J$ zq94j7fKej^@3*_a1*4>ZE>MqZ?CC)^mtlh(%mb)Koi1nMzfza371cV?`&COqyMa*g z(?AL+sWHNmj4*v%F|BC}S@V>awt`0dS?F(uWgt#Q7ATjFHlqq-?UdzOWpD4Lagoq;MoK=GIWCF3x9oCq6 zbx&{|^W3q@xVs>{8bUauY>DfR;MJ_jsv`1doqQjErK})jJyM&x#Q2=KIFZW}Ik_OG z6f9}SIr*>#iE`$&f}B|pxYW}CICS%f-Lg81CpgeFqe`>`;)bya9fMb#)4C3UFxa7djj zkcfgD7LJckrZI@#pRQd1P>R2}d#Af(&1>@)w0@{>Pe%EGH|&AU

    {(~CP&Czdsf+= zTQ+A}IfHGv@J>hgQ8werC^Og0Ax0ab)((^5+52$$4*|%306X#<(v^ixBdL>87L^X~ z$#`hLQ_k`W?zHwt4A~Iz?_zl*PDk^k11(7JI~4v=YIB*<(AD8@Yv)L^0O&eal5!?x zjG>H5@SnY?eb&vZco{JzfNKBJ9~%4tH*hI~(nF$hSdTUeQ$B4nNB0-iu;LG(2Hm42 z);UXE-7NrxSfPXsfo2)wI8u*Tg1ZKHO4v?lj82b)@rAm>$*{PGNJX!VoHUk7qe89< zFg{I2=;nG13~(g$!(wWg_Zg1i7#F#!)eZemhlJ3}Rfx{526t8>-Re%4_(6qOpI!2` zk~yv<|0$XmiqdA=dEs3Zxkt0K$j;D>Snn6}fTmIWxh$`-R1lv72~Rm`oq?&`Fgs4s zZJbPzbWVvhCCW3dPc~coMX|xz?(tbbwEWA}?o4PppDzGB3NT>NCYh%cTBKF-MqtjYb4m3LlVoby z6*D_D+@#47j#Sn-!fgU;>_kwST)nQF19PK8$6)0!MPcV$6-wiRzt@ru$hCwV|8rSq zq&mtrgX@l_8?~lIGeRvIHaf&`!G{O2nUNZ1_smoyz8btBCz8c58E zWKs8|q2!UVpG>b8_Vq*E#1x0FGlq$e^&D~wS>*v zptYn?wmn#0r}sM|DnDV~s^u)_`Z7KMBcJvp$5g(R@1Xy*x}2^%lAKYOGxhVVx}04{ zAzFx6{5y5INbg--m*x6-NnI|jqZ@p;B3D-M=YzUOHQNa2{h`_*#48Ce$TfAjx-M7M z&2_Y{B$LRmTFkw;_)vkAU{V(7+VAOezWk$V{>fbB%9#fX@<@T}Pssu1K-o>s5xGbR z_V=Ji!Kxl7qnV)A%N04!$qNO1$Xm#t3Wh6gDIqPXny*#m_^N!pDkoL#Sh=bw%Zu1^ zpGBx&)*{EZ5EsM=s8MeiM5?hJUC3=tYmqb66?j&QoZTW<6y?^8bYtveDqXKS-hL1z zd%00*jEoIw_F9-b>&&hx%1yp#;|;#>5%PngTu*_bLzz940AGOF$ z`o15x$jvQgn%y~kpO^Zo1G3Q?q&=JWbwix^c1cJ|!;ON+$r&- zwk0afB=9iCf~yuZ9(!1ZcpqdU?m}&57^n&ehdfrQm8Ar8)O| z${qmQktI1-`Ji7yz%253o@e%md5($T%${bCO3)pXgTW#y)Ppt-dwKdE{M9G*kqE#1fMP2DKO zOo^`Ug8)0Dp5h^}gn&haJ4Qod06~ez00*2@?tM+1?v>+IM<3bogO5I0W%S8_(QEKx zv3y@tBt?j%t7YU@>{r{B1VAs|Xio}mPrHZG3o#9d0naJl^*JDMG1qlI5m%6l-6Mh| z48Z9QZdUKIc)D<@ic@2jwAf=?Fn&J;3O}tj84Ly(5gC4}B2Q@ez?qBu4h47VK6UOc zu@8%UfOjpa$jKG=Y`J0bK2tVtlreda#c~s5r&vIFjG&%vBy+HDjRxR=*meMUd;D!L z3;UD+$Tn2F3G#k3%UiMR;eiIkRO3)KAulVqEj_ffHj^bROLcj7wB!XK7Ij)YX#V1W{iCF;3~^@+P%cQR;8P{QJFJG8D^ zqvlu+XQM~BkLi&Sx~6}bOkS#y5n?Kw7cC{We6@^dAS?v%q{{E%_UxzBN`q2$PIG^c ziZ$r-enz`=&*~l-_e;>W-fIb#yCx||x;Zx+34)+I0B%X%B47XroLuJQa%V4d=1M0c z?Khly0k8IGWA0w-e#jI(b8vr@E}5k?_1O2`qhf;-cmp`4R6@?QH~J z^Wo%(n(XPsDBZ`pv0>CR8&a%M$UG`-7+*C zULB^s`4ZcvFg=F0fQf|ZK_rWP#vTrnCG3(iK&ASo>Qqk`kTI{5x2jr|30In}pD?o0 zoGnkFC24(By9U>#m*7Tuta4!2xYmhSZtx1o0^-z!NoQG@u&fukx z$@mH;v^-fbzX+Rp&l6}A@_1o@=JP}W)IA!?W9rcRLuek?&wC4U7go7tAM_XX`(HxC zp34YA`G=t`yFwKnvY4+_g@?@#a(@1rDlROU5A7SF?Co`(=OXhg>lB;1&Ozosp?QNx za*H#!t3Io$j&_P&S>y$HLqYS{@UaX-Q<=hq9phb1#E3P)17Jk5m+Xzi`EF#cR$o~s zqA@~}qmE#CX!=(szmDWLssa38B)^SNovtm&k1|!EBJ-wd@w>BFWyk<619C*m*w$+q zy>5irlnAodE2aS8g1u;M^a|bPyI-roRn3^2I)xj~NE;eOp{Y@1XcUtsGlnTc(I8ZN zFx)|30MMQ6MNFqKlAJFsVu~&+HcSoI8=(-RxEwv`(^0on%0r z5_Lu%Wfx1kzKn)+(V)K6?QVA?h{$*Ffyt@g^*K#NG1t1?iF&{-*AXuVsJrExko>qQ zUN*34L(`RJ)32yaxT<`4pl0u{ts-AW!o51>>au*-+i3Vg+T}m2%Qfyl#-7A}Ey$J! zN;n_fb)|-E`CiG~T|&0pU-Gi$L~E~OgC%s4KGDK*0nF2W;c~IH7pcLvCgE~8%&~8#JVg)6`?~iZw4=c~y7vSxvayO_P1Xh4rWdh06`^k#I2xm&%~R<%>&+ zs9uXNKPQ9`gpy|qA@oyQr9n9bPasfS=!J{YWR&h+-7VvO30gzAL~ZYla5=IeT#jbN z{3_CgO*`VBG$f45{$DwBU%!N5VMHz^R;q+~PUO{G!u-5n!uViUpM?2`lf~%RUc#WO z<#iXUA20Vr9@?~te+FaelQ84Fgz;gUT*7>7ttCuhEhJ1E=pcKY2%(K?!DC^vcvT5= zqDq*RDq;S%nuNIu36uKzBrY_VM-)XMNLht464$uaSzc~12%Y6A&kG#HCD|HDqDlT* zqRAfVIXlue+W6rYrX<;ZCBY#5Rlk&ZR2e$Pt9LGV`}$FpGJbD!o;(?vr&OBUU67xt zlwpsZSM>W|L&HzWsFeAak$)R|gGw1z+uW#9=Iea{rhn&B=D(p`%$^Wl%Df&Kve){h z%oo{0$Sg)k$j_X)b5$wxF?l4Db8;!uizWC8FJ-0;B4w_L%ylYdGI=SJD>7g9lg#bS zRo)Qu?1no=#Xh<7N94WA9a0UEdh_MVg4UTY-;T^N%y_deizksgURhD=_7z$lD0e=H z+~Hx&3SpHyH~jxDcP#Q|@@le2_lxLDaGd^xra6xC=W+gA_UFW(H}Yq~Hs#5dUd zpGIe!XFpv-vm$c!Y2y5e-4)TLdKOz(c+thRmW+Ac&J~UrT_nljxWh`vq{Ow}OE_YV z!miFgOL$h-RvXXTg6^cx7S&DJ6vk}F=q;*F`KNwjhi?)d=`GZURJFxRa}<_?Sy7eC zas%x|em)3>ZTVp#Y*c0R%Df+>C3#*Mfoi+Yi6MU)ZTCEWw<&C4$+9lsa%jw&oP`ny zH-AbC%W@H1SeJ^qM9=mh47ts5Umqg^14B1q(b0#E9|E{eprD=22W$@-P*n~x3wbdf zBJzC}xr4EoSVx2%a*@b)^1ABSDWkutZ9SCCKYoqF;G8w%(R;RN=F|MmGpIU)Z6WYC z(`{$QnYP%+31af|L;DbP)!2tasWq+U`%zig;abS%aqX=n3& z86mx9q~UP<(p9Y9jd93IJS53PGa;UACKaZ-DGsVi&}iy>vX*xf^0PO|>1wQ}nHl~( z+MhS}=X@*I)L-Y67OqZL^|hd@WHq`9*FaZFTSe$fpS_)~I@p9^4TSYmPXfR7KRN|v zy(Y3rl#^B~z#gXgBexp!Vp;yATiG~%Q!J;f2@n=uJ^l#70@3jes@m9WsWe4WiEePm zFHAg)tXqYqMk`4@1;;ezX~WWf&hDW62d^Nitw=V_N$O-{&c

    =LT7Q-I(L?Cwap9 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

    h$y4YfdY$x-kPt)?tVt4G1q~bq%cJk49lv+} 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ⅇ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

    R`AO^+8%=%7pDl!>1N%))i0rj5;!XjD$K&!~U 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

    9~zwN7}RQXt{s|KH#8>DypV`*V1^ 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Ⓢ)-cAZuTkYBU91$mm?FS2&JEz;?7-`<(dor9Qv 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 z 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 z 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

    B7pQ`(rWQKN!7(#)B$PlV$`ZlhmIyq^hSObsZzX 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

  2. ?qX|L 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